fix(site-builder): editor canvas renders content with the published base reset
Published pages/Preview ship a head reset (*{margin:0;padding:0}, img block,
a inherit, body line-height 1.6 / #1f2937 / 16px); the editor canvas had none,
so h1-h6/p margins, line-height and inherited size/colour differed.
- editor.css: one commented block scoping the reset to .canvas-device-frame,
every selector zero-specificity (:where) so inline styles / class rules win
exactly as when published; editor chrome untouched (snapshot identical).
- tokens.ts: canvas heading/link token rules made zero-specificity too.
- utils/css-reset.ts: single source for the reset declarations (html-export
imports it; test asserts byte-identity with the backend head and that the
editor.css block carries the same declarations).
- scripts/e2e-canvas-parity.mjs (npm run test:browser:canvas): real editor vs
published HTML in Firefox + Chromium, boxes within 1px + computed styles.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
76f0ba9ca1
commit
dc415db2d3
15 files changed
+774
-6
No files matched your search
@@ -11,6 +11,7 @@
|
||||
"test:headed": "playwright test tests/site-builder.spec.ts --reporter=list --headed",
|
||||
"test:e2e:sitesmith": "playwright test tests/sitesmith.spec.ts --reporter=list",
|
||||
"test:unit": "vitest run",
|
||||
"test:browser:canvas": "node scripts/e2e-canvas-parity.mjs",
|
||||
"test:unit:watch": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Bundled (esbuild) by scripts/e2e-canvas-parity.mjs and run in Node: renders what a publish
|
||||
// of the CURRENT stored state would emit, composed like TopBar's Preview
|
||||
// (header + body + footer under the real document head). Used for specs whose
|
||||
// checked-in published HTML is stale relative to the stored state.
|
||||
import { exportBodyHtml, exportComposedPreviewHtml } from '../src/utils/html-export';
|
||||
|
||||
export function renderPublished(project: any, pageIdx: number): string {
|
||||
const page = project.pages_craft_state[pageIdx];
|
||||
const hdr = project.header_craft_state ? exportBodyHtml(project.header_craft_state, 'header').html : '';
|
||||
const ftr = project.footer_craft_state ? exportBodyHtml(project.footer_craft_state, 'footer').html : '';
|
||||
const body = exportBodyHtml(page.craftState).html;
|
||||
return exportComposedPreviewHtml(page.craftState, hdr + body + ftr, {
|
||||
title: page.name, includeFonts: true, headCode: project.design?.headCode, design: project.design,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Real-browser regression test: the editor CANVAS must lay page content out
|
||||
* exactly like the PUBLISHED page ("the site looks different in the builder
|
||||
* than published"). jsdom has no layout, so this mounts the REAL editor (a
|
||||
* vite build served by a tiny mock API) in headless Firefox AND Chromium,
|
||||
* loads a stored project, and compares -- element by element -- against the
|
||||
* same page's published HTML rendered in a plain page whose viewport equals
|
||||
* the canvas content width.
|
||||
*
|
||||
* Compared per matched element: bounding box (x/y/w/h, within 1px) and the
|
||||
* computed font-size / line-height / margin / padding / colour / font-family
|
||||
* / font-weight / display. Editor-only chrome (the dashed header/footer zone
|
||||
* separators) is compensated for, not ignored.
|
||||
*
|
||||
* npm run test:browser:canvas
|
||||
*
|
||||
* Env:
|
||||
* CHROMIUM_PATH / FIREFOX_PATH override the Playwright-pinned binaries
|
||||
* WHP_CANVAS_SPECS extra comma-separated spec.json paths
|
||||
* (e.g. real customer sites kept out of git)
|
||||
* CANVAS_SHOTS=<dir> write canvas + published PNGs there
|
||||
* CANVAS_CHROME_DUMP=<file> write a computed-style snapshot of the
|
||||
* editor chrome (everything OUTSIDE the
|
||||
* canvas frame) to diff before/after a change
|
||||
* CANVAS_ONLY=chromium|firefox run a single browser
|
||||
*
|
||||
* A spec is {name, project: <project.json>, published: {<slug>: <full html>}}
|
||||
* with paths relative to the spec file. Only pages that have a published
|
||||
* file are compared.
|
||||
*/
|
||||
import { chromium, firefox } from '@playwright/test';
|
||||
import { build as esbuild } from 'esbuild';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import http from 'node:http';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const VIEW_H = 1000;
|
||||
const EDITOR_W = 1500;
|
||||
const TOL = 1; // px
|
||||
|
||||
// ---------------------------------------------------------------- specs
|
||||
const specPaths = [
|
||||
path.join(root, 'tests/fixtures/canvas-parity/ols.spec.json'),
|
||||
...(process.env.WHP_CANVAS_SPECS || '').split(',').map((s) => s.trim()).filter(Boolean),
|
||||
];
|
||||
// `published` is either {slug: html file} or "generate" = render what a publish of the stored
|
||||
// state emits (scripts/e2e-canvas-export-entry.ts) -- for sites whose saved HTML is stale.
|
||||
let renderPublished = null;
|
||||
async function loadExporter() {
|
||||
if (renderPublished) return renderPublished;
|
||||
const out = await esbuild({
|
||||
entryPoints: [path.join(root, 'scripts/e2e-canvas-export-entry.ts')],
|
||||
bundle: true, write: false, format: 'esm', platform: 'node', jsx: 'automatic', logLevel: 'error',
|
||||
define: { 'process.env.NODE_ENV': '"production"', __EDITOR_BUILD__: '"test"' },
|
||||
banner: { js: "import { createRequire } from 'node:module'; const require = createRequire(import.meta.url);" },
|
||||
});
|
||||
const f = path.join(os.tmpdir(), `wsb-export-${process.pid}.mjs`);
|
||||
fs.writeFileSync(f, out.outputFiles[0].text);
|
||||
renderPublished = (await import(f)).renderPublished;
|
||||
fs.rmSync(f, { force: true });
|
||||
return renderPublished;
|
||||
}
|
||||
const specs = [];
|
||||
for (const p of specPaths) {
|
||||
const dir = path.dirname(p);
|
||||
const s = JSON.parse(fs.readFileSync(p, 'utf8'));
|
||||
const project = JSON.parse(fs.readFileSync(path.resolve(dir, s.project), 'utf8'));
|
||||
let published;
|
||||
if (s.published === 'generate') {
|
||||
const render = await loadExporter();
|
||||
const only = s.pages || project.pages_craft_state.map((pg) => pg.slug);
|
||||
published = {};
|
||||
project.pages_craft_state.forEach((pg, i) => { if (only.includes(pg.slug)) published[pg.slug] = render(project, i); });
|
||||
} else {
|
||||
published = Object.fromEntries(Object.entries(s.published).map(([k, v]) => [k, fs.readFileSync(path.resolve(dir, v), 'utf8')]));
|
||||
}
|
||||
specs.push({ name: s.name, project, published });
|
||||
}
|
||||
|
||||
// A stored `style` often carries a shorthand plus BLANK longhands
|
||||
// ({padding:'20px', paddingTop:''}). The editor renders those through React, which
|
||||
// assigns '' to the longhand and thereby wipes the shorthand in the canvas (the
|
||||
// export drops blanks, so published keeps it). That is a SEPARATE bug from the
|
||||
// base-reset parity this test guards (same class as fix/bg-shorthand-longhand), so by
|
||||
// default the blanks are stripped from the loaded state to isolate the reset.
|
||||
// CANVAS_RAW=1 keeps the stored state verbatim (shows that bug's residue).
|
||||
function stripBlankStyles(project) {
|
||||
if (process.env.CANVAS_RAW) return project;
|
||||
const clean = (json) => {
|
||||
const nodes = JSON.parse(json);
|
||||
for (const n of Object.values(nodes)) {
|
||||
const st = n && n.props && n.props.style;
|
||||
if (st && typeof st === 'object') for (const k of Object.keys(st)) if (st[k] === '') delete st[k];
|
||||
}
|
||||
return JSON.stringify(nodes);
|
||||
};
|
||||
const out = { ...project };
|
||||
for (const k of ['craft_state', 'header_craft_state', 'footer_craft_state']) if (typeof out[k] === 'string' && out[k]) out[k] = clean(out[k]);
|
||||
out.pages_craft_state = (project.pages_craft_state || []).map((pg) => ({ ...pg, craftState: typeof pg.craftState === 'string' ? clean(pg.craftState) : pg.craftState }));
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- build + serve the real editor
|
||||
const buildDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wsb-canvas-'));
|
||||
execFileSync(path.join(root, 'node_modules/.bin/vite'), ['build', '--outDir', buildDir, '--emptyOutDir', '--logLevel', 'error'], { cwd: root, stdio: 'inherit' });
|
||||
|
||||
let currentProject = null;
|
||||
const mime = { '.js': 'text/javascript', '.css': 'text/css', '.html': 'text/html', '.png': 'image/png', '.svg': 'image/svg+xml' };
|
||||
const server = http.createServer((req, res) => {
|
||||
const u = new URL(req.url, 'http://x');
|
||||
if (u.pathname === '/api/site-builder.php') {
|
||||
res.setHeader('content-type', 'application/json');
|
||||
if (u.searchParams.get('action') === 'load') {
|
||||
return res.end(JSON.stringify({ success: true, rev: 'r1', limits: {}, project: currentProject }));
|
||||
}
|
||||
return res.end(JSON.stringify({ success: true }));
|
||||
}
|
||||
const p = u.pathname === '/' ? '/index.html' : u.pathname;
|
||||
if (p === '/index.html') {
|
||||
const cfg = { user: 't', apiUrl: '/api/site-builder.php', csrfToken: 'x', siteId: 1, siteDomain: 'example.test', siteName: 'Parity', backUrl: '/', isRoot: false };
|
||||
const h = fs.readFileSync(path.join(buildDir, 'index.html'), 'utf8')
|
||||
.replace('</head>', () => `<script>window.WHP_CONFIG=${JSON.stringify(cfg)};</script></head>`);
|
||||
res.setHeader('content-type', 'text/html');
|
||||
return res.end(h);
|
||||
}
|
||||
const f = path.join(buildDir, p);
|
||||
if (f.startsWith(buildDir) && fs.existsSync(f) && fs.statSync(f).isFile()) {
|
||||
res.setHeader('content-type', mime[path.extname(f)] || 'application/octet-stream');
|
||||
return res.end(fs.readFileSync(f));
|
||||
}
|
||||
res.statusCode = 404;
|
||||
res.end('nf');
|
||||
});
|
||||
await new Promise((r) => server.listen(0, '127.0.0.1', r));
|
||||
const base = `http://127.0.0.1:${server.address().port}`;
|
||||
|
||||
// ---------------------------------------------------------------- in-page measurer (runs in BOTH documents)
|
||||
function measure({ rootSel, isCanvas, skipNav }) {
|
||||
const rootEl = document.querySelector(rootSel);
|
||||
const rr = rootEl.getBoundingClientRect();
|
||||
const bl = isCanvas ? parseFloat(getComputedStyle(rootEl).borderLeftWidth) || 0 : 0;
|
||||
const bt = isCanvas ? parseFloat(getComputedStyle(rootEl).borderTopWidth) || 0 : 0;
|
||||
const ox = rr.left + bl;
|
||||
const oy = rr.top + bt;
|
||||
// Editor-only dashed zone separators shift everything after them by their width.
|
||||
const zones = isCanvas ? [...rootEl.querySelectorAll('[data-zone-preview]')] : [];
|
||||
const adjustY = (el) => {
|
||||
let adj = 0;
|
||||
for (const z of zones) {
|
||||
const cs = getComputedStyle(z);
|
||||
const zone = z.getAttribute('data-zone-preview');
|
||||
const bw = parseFloat(zone === 'header' ? cs.borderBottomWidth : cs.borderTopWidth) || 0;
|
||||
if (zone === 'header' && !z.contains(el) && (z.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING)) adj += bw;
|
||||
if (zone === 'footer' && (z === el || z.contains(el))) adj += bw;
|
||||
}
|
||||
return adj;
|
||||
};
|
||||
const SKIP = new Set(['STYLE', 'SCRIPT', 'NOSCRIPT', 'LINK', 'META', 'BR', 'HEAD', 'TITLE', 'PATH', 'CIRCLE', 'RECT', 'LINE', 'POLYLINE', 'POLYGON', 'G', 'USE', 'DEFS']);
|
||||
const norm = (s) => s.replace(/\s+/g, '');
|
||||
// Visible text only (never <style>/<script> bodies), whitespace-insensitive:
|
||||
// React renders no inter-element whitespace, the stored HTML does.
|
||||
const textOf = (el) => {
|
||||
let t = '';
|
||||
const w = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
||||
for (let n = w.nextNode(); n; n = w.nextNode()) {
|
||||
const pt = n.parentElement && n.parentElement.tagName;
|
||||
if (pt !== 'STYLE' && pt !== 'SCRIPT') t += n.nodeValue;
|
||||
}
|
||||
return norm(t);
|
||||
};
|
||||
const normStyle = (s) => norm((s || '').replace(/\s*([:;])\s*/g, '$1').replace(/;$/, ''));
|
||||
// Order-insensitive identity of an element's inline style (React and the exporter
|
||||
// serialise the same declarations in different orders); editor-only props dropped.
|
||||
const EDITOR_PROPS = /^(cursor|outline|user-select|pointer-events|position)/;
|
||||
const styleKey = (el) => Array.from(el.style).filter((p) => !EDITOR_PROPS.test(p)).sort()
|
||||
.map((p) => `${p}:${el.style.getPropertyValue(p)}`).join(';').replace(/\s+/g, '').slice(0, 120);
|
||||
const counts = new Map();
|
||||
const out = {};
|
||||
const walk = (el) => {
|
||||
for (const c of el.children) {
|
||||
if (SKIP.has(c.tagName.toUpperCase())) continue;
|
||||
const tag = c.tagName.toLowerCase();
|
||||
// Navbar link ORDER follows page order, which the harness rotates to open page N: skip links.
|
||||
if (skipNav && tag === 'a' && c.closest('nav')) continue;
|
||||
// Editor-only wrapper boxes (Craft frame div, RenderNode `position:relative` host, header/footer zone-preview host) have no
|
||||
// counterpart in the published DOM: descend through them without measuring.
|
||||
const bare = tag === 'div' && (c.attributes.length === 0
|
||||
|| normStyle(c.getAttribute('style')) === 'max-width:1200px;margin:0auto'
|
||||
|| (isCanvas && c.attributes.length === 1 && c.hasAttribute('style') && normStyle(c.getAttribute('style')) === 'position:relative'));
|
||||
if (c.hasAttribute('data-zone-preview') || bare) { walk(c); continue; }
|
||||
const text = textOf(c).slice(0, 40);
|
||||
const ident = tag === 'img' ? `${c.getAttribute('alt') || ''}`
|
||||
: text || styleKey(c);
|
||||
const k0 = `${tag}|${ident}`;
|
||||
const n = (counts.get(k0) || 0) + 1;
|
||||
counts.set(k0, n);
|
||||
const key = `${k0}#${n}`;
|
||||
const r = c.getBoundingClientRect();
|
||||
const cs = getComputedStyle(c);
|
||||
out[key] = {
|
||||
tag,
|
||||
x: r.left - ox, y: r.top - oy - adjustY(c), w: r.width, h: r.height,
|
||||
fontSize: cs.fontSize, lineHeight: cs.lineHeight, fontFamily: cs.fontFamily, fontWeight: cs.fontWeight,
|
||||
color: cs.color, display: cs.display, textAlign: cs.textAlign,
|
||||
margin: `${cs.marginTop} ${cs.marginRight} ${cs.marginBottom} ${cs.marginLeft}`,
|
||||
padding: `${cs.paddingTop} ${cs.paddingRight} ${cs.paddingBottom} ${cs.paddingLeft}`,
|
||||
};
|
||||
walk(c);
|
||||
}
|
||||
};
|
||||
walk(rootEl);
|
||||
return out;
|
||||
}
|
||||
|
||||
const PROPS = ['fontSize', 'lineHeight', 'fontFamily', 'fontWeight', 'color', 'display', 'textAlign', 'margin', 'padding'];
|
||||
|
||||
function chromeSnapshot() {
|
||||
const frame = document.querySelector('.canvas-device-frame');
|
||||
const rows = {};
|
||||
let i = 0;
|
||||
for (const el of document.body.querySelectorAll('*')) {
|
||||
if (frame && (frame === el || frame.contains(el))) continue;
|
||||
if (el.closest('[data-zone-preview]')) continue;
|
||||
const cs = getComputedStyle(el);
|
||||
const k = `${i++}:${el.tagName.toLowerCase()}.${String(el.className && el.className.baseVal !== undefined ? el.className.baseVal : el.className).slice(0, 40)}`;
|
||||
rows[k] = [cs.margin, cs.padding, cs.fontSize, cs.lineHeight, cs.color, cs.display, cs.fontFamily].join('|');
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- driver
|
||||
async function run(name, launcher, exe) {
|
||||
const browser = await launcher.launch({ executablePath: exe || undefined, ...(name === 'chromium' ? { args: ['--no-sandbox'] } : {}) });
|
||||
let failed = 0;
|
||||
const offline = (route) => route.abort();
|
||||
const shotDir = process.env.CANVAS_SHOTS;
|
||||
if (shotDir) fs.mkdirSync(shotDir, { recursive: true });
|
||||
|
||||
for (const spec of specs) {
|
||||
const pages = spec.project.pages_craft_state || [];
|
||||
for (const [idx, pg] of pages.entries()) {
|
||||
const pubHtml = spec.published[pg.slug];
|
||||
if (!pubHtml) continue;
|
||||
const label = `${spec.name}/${pg.slug}`;
|
||||
// The editor opens pages_craft_state[0]; rotate so page `idx` is first.
|
||||
currentProject = stripBlankStyles({ ...spec.project, pages_craft_state: [pg, ...pages.filter((_, j) => j !== idx)] });
|
||||
|
||||
const ctx = await browser.newContext({ viewport: { width: EDITOR_W, height: VIEW_H } });
|
||||
await ctx.route(/^https?:\/\/(?!127\.0\.0\.1)/, offline); // no web fonts / icon CDN: identical fallbacks both sides
|
||||
const ed = await ctx.newPage();
|
||||
const errors = [];
|
||||
ed.on('pageerror', (e) => errors.push(String(e)));
|
||||
await ed.goto(base + '/');
|
||||
await ed.waitForSelector('.canvas-device-frame [data-craft-container]', { timeout: 20000 });
|
||||
await ed.waitForTimeout(1500); // header/footer zone previews + layout settle
|
||||
const canvasW = await ed.evaluate(() => document.querySelector('.canvas-device-frame').clientWidth);
|
||||
const canvas = await ed.evaluate(measure, { rootSel: '.canvas-device-frame', isCanvas: true, skipNav: idx > 0 });
|
||||
if (shotDir) await ed.screenshot({ path: path.join(shotDir, `${label.replace('/', '_')}.${name}.canvas.png`) });
|
||||
if (process.env.CANVAS_CHROME_DUMP && idx === 0 && spec === specs[0]) {
|
||||
fs.writeFileSync(`${process.env.CANVAS_CHROME_DUMP}.${name}.json`, JSON.stringify(await ed.evaluate(chromeSnapshot), null, 1));
|
||||
}
|
||||
const chromeBody = await ed.evaluate(() => getComputedStyle(document.body).fontSize);
|
||||
|
||||
// Published: viewport so that the layout width (excluding any scrollbar) equals the canvas content width.
|
||||
let w = canvasW;
|
||||
const pub = await ctx.newPage();
|
||||
let published;
|
||||
for (let attempt = 0; attempt < 3; attempt++) {
|
||||
await pub.setViewportSize({ width: w, height: VIEW_H });
|
||||
await pub.setContent(pubHtml, { waitUntil: 'domcontentloaded' });
|
||||
await pub.waitForTimeout(300);
|
||||
const cw = await pub.evaluate(() => document.documentElement.clientWidth);
|
||||
if (cw === canvasW) break;
|
||||
w += canvasW - cw;
|
||||
}
|
||||
published = await pub.evaluate(measure, { rootSel: 'body', isCanvas: false, skipNav: idx > 0 });
|
||||
if (shotDir) await pub.screenshot({ path: path.join(shotDir, `${label.replace('/', '_')}.${name}.published.png`) });
|
||||
|
||||
const pk = Object.keys(published);
|
||||
const matched = pk.filter((k) => k in canvas);
|
||||
const diffs = [];
|
||||
for (const k of matched) {
|
||||
const a = canvas[k], b = published[k];
|
||||
for (const f of ['x', 'y', 'w', 'h']) if (Math.abs(a[f] - b[f]) > TOL) diffs.push([k, f, a[f].toFixed(1), b[f].toFixed(1)]);
|
||||
for (const f of PROPS) if (a[f] !== b[f]) diffs.push([k, f, a[f], b[f]]);
|
||||
}
|
||||
const ratio = matched.length / pk.length;
|
||||
const bad = new Set(diffs.map((d) => d[0]));
|
||||
const ok = diffs.length === 0 && ratio >= 0.6 && errors.length === 0 && chromeBody === '13px';
|
||||
if (!ok) failed++;
|
||||
console.log(`[${name}] ${ok ? 'PASS' : 'FAIL'} ${label}: ${matched.length}/${pk.length} published elements matched, ${bad.size} elements differ (${diffs.length} property diffs), canvas width ${canvasW}, editor body font ${chromeBody}`);
|
||||
if (diffs.length) {
|
||||
// Summarise: property -> count + first examples
|
||||
const byProp = {};
|
||||
for (const [k, f, a, b] of diffs) (byProp[f] ||= []).push(`${k.split('#')[0].slice(0, 44)} canvas=${a} published=${b}`);
|
||||
for (const [f, rows] of Object.entries(byProp)) console.log(` ${f}: ${rows.length}x e.g. ${rows.slice(0, 3).join(' ;; ')}`);
|
||||
}
|
||||
if (errors.length) console.log(' page errors:', errors.join('\n'));
|
||||
if (process.env.CANVAS_JSON) {
|
||||
fs.writeFileSync(`${process.env.CANVAS_JSON}.${name}.${label.replace('/', '_')}.json`, JSON.stringify({ canvas, published, diffs }, null, 1));
|
||||
}
|
||||
await ctx.close();
|
||||
}
|
||||
}
|
||||
await browser.close();
|
||||
return failed;
|
||||
}
|
||||
|
||||
let total = 0;
|
||||
try {
|
||||
if (process.env.CANVAS_ONLY !== 'firefox') total += await run('chromium', chromium, process.env.CHROMIUM_PATH);
|
||||
if (process.env.CANVAS_ONLY !== 'chromium') total += await run('firefox', firefox, process.env.FIREFOX_PATH);
|
||||
} finally {
|
||||
server.close();
|
||||
fs.rmSync(buildDir, { recursive: true, force: true });
|
||||
}
|
||||
console.log(total ? `FAILED: ${total} page comparison(s) differ` : 'OK: canvas content matches published');
|
||||
process.exit(total ? 1 : 0);
|
||||
@@ -391,6 +391,50 @@ body {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
CANVAS = PUBLISHED BASE RESET
|
||||
Published pages and Preview ship a reset in the document head
|
||||
(utils/css-reset.ts CSS_RESET: `*{margin:0;padding:0}`, img{display:block},
|
||||
a{color:inherit}, body line-height 1.6 / colour #1f2937 / 16px). The editor
|
||||
canvas lives inside the editor document, whose own body is 13px editor
|
||||
chrome with no such reset, so content looked different (default h1-h6/p/ul
|
||||
margins, `normal` line-height, 13px inherited text). This block gives the
|
||||
canvas FRAME -- the <body> stand-in -- and everything inside it the same
|
||||
defaults.
|
||||
Rules:
|
||||
- Every selector is ZERO specificity (:where). The published reset is `*`
|
||||
(specificity 0), so component inline styles and class rules beat it; a
|
||||
plain `.canvas-device-frame *` (0,1,0) would instead beat component
|
||||
<style> class rules and editor chrome rules. Do NOT remove the :where().
|
||||
- Scoped to the frame, never body/:root, so panels, toolbars and Craft
|
||||
overlays (all rendered outside the frame) are untouched. Editor-only
|
||||
elements inside the frame style themselves with classes / inline styles,
|
||||
which outrank this block.
|
||||
- Declarations come from utils/css-reset.ts; css-reset.test.ts fails if
|
||||
this block and that module drift apart. The font-family / colour tokens
|
||||
are layered on top by CanvasTokenStyle exactly as the published head
|
||||
layers them over the reset.
|
||||
========================================================================== */
|
||||
.canvas-device-frame {
|
||||
font-size: 16px; /* UA default a published <body> gets; editor body is 13px */
|
||||
font-family: 'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
|
||||
line-height:1.6;
|
||||
color:#1f2937;
|
||||
-webkit-font-smoothing:antialiased;
|
||||
}
|
||||
:where(.canvas-device-frame) :where(*),
|
||||
:where(.canvas-device-frame) ::before,
|
||||
:where(.canvas-device-frame) ::after {
|
||||
box-sizing:border-box;margin:0;padding:0;
|
||||
}
|
||||
:where(.canvas-device-frame) :where(img) {
|
||||
max-width:100%;height:auto;display:block;
|
||||
}
|
||||
:where(.canvas-device-frame) :where(a) {
|
||||
color:inherit;
|
||||
}
|
||||
/* END CANVAS = PUBLISHED BASE RESET */
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
No Selection Message
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
// @ts-ignore -- node builtin (vitest runs in node); the project has no @types/node
|
||||
import fs from 'node:fs';
|
||||
import {
|
||||
CSS_RESET, RESET_ALL_DECLS, BODY_BASE_DECLS, IMG_DECLS, LINK_DECLS,
|
||||
} from './css-reset';
|
||||
import { buildCanvasTokenCss } from './tokens';
|
||||
import { DEFAULT_SITE_DESIGN } from '../state/SiteDesignContext';
|
||||
|
||||
// (not `?raw`: vitest's css pipeline strips/empties .css imports, and the block markers are comments)
|
||||
const css: string = fs.readFileSync('src/styles/editor.css' /* vitest cwd = craft/ */, 'utf8');
|
||||
const compact = (s: string) => s.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\s+/g, '').replace(/;}/g, '}');
|
||||
const blockStart = css.lastIndexOf('/*', css.indexOf('CANVAS = PUBLISHED BASE RESET'));
|
||||
const block = compact(css.slice(blockStart, css.indexOf('END CANVAS = PUBLISHED BASE RESET')));
|
||||
|
||||
describe('published reset text', () => {
|
||||
test('CSS_RESET is byte-identical to the backend head text (site-builder.php $reset)', () => {
|
||||
expect(CSS_RESET).toBe(
|
||||
"*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{overflow-x:clip}" +
|
||||
"body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;color:#1f2937;-webkit-font-smoothing:antialiased}" +
|
||||
"img{max-width:100%;height:auto;display:block}a{color:inherit}",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('editor.css canvas reset block', () => {
|
||||
test('block exists', () => expect(blockStart).toBeGreaterThan(-1));
|
||||
test.each([
|
||||
['reset-all', RESET_ALL_DECLS],
|
||||
['body base', BODY_BASE_DECLS],
|
||||
['img', IMG_DECLS],
|
||||
['a', LINK_DECLS],
|
||||
])('carries the shared %s declarations', (_n, decls) => {
|
||||
expect(block).toContain(compact(decls));
|
||||
});
|
||||
test('base font-size matches the default 16px a published body gets', () => {
|
||||
expect(block).toContain('font-size:16px');
|
||||
});
|
||||
test('every selector in the block is zero-specificity (:where) below the frame', () => {
|
||||
const rules = css.slice(blockStart, css.indexOf('/* END CANVAS = PUBLISHED BASE RESET'))
|
||||
.replace(/\/\*[\s\S]*?\*\//g, '');
|
||||
const selectors = [...rules.matchAll(/([^{}]+)\{/g)].map((m) => m[1].trim());
|
||||
expect(selectors.length).toBeGreaterThan(3);
|
||||
for (const sel of selectors) for (const part of sel.split(',')) {
|
||||
const p = part.trim();
|
||||
if (p === '.canvas-device-frame') continue; // the frame itself is the <body> stand-in
|
||||
expect(p.startsWith(':where(.canvas-device-frame)')).toBe(true);
|
||||
// nothing after the :where() group may add specificity (classes, ids, attrs, :not...)
|
||||
expect(p.replace(/:where\([^)]*\)/g, '').replace(/::(before|after)/g, '')).not.toMatch(/[.#[:]/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('canvas token base css keeps published (zero) specificity', () => {
|
||||
test('link/heading rules are :where-wrapped, only the frame rule carries a class', () => {
|
||||
const out = buildCanvasTokenCss(DEFAULT_SITE_DESIGN);
|
||||
expect(out).toContain(':where(.canvas-device-frame) :where(a){color:var(--wsb-link)}');
|
||||
expect(out).toContain(':where(.canvas-device-frame) :where(h1,h2,h3,h4,h5,h6){font-family:var(--wsb-heading-font)}');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* The page-level base reset shared by the PUBLISHED/Preview document head
|
||||
* (html-export.ts `wrapInDocument`, mirrored by the backend's
|
||||
* `generateCompiledHTML` in web-files/api/site-builder.php) and the editor
|
||||
* CANVAS (the "canvas = published reset" block in styles/editor.css).
|
||||
*
|
||||
* The declaration strings live here once so the two cannot drift;
|
||||
* css-reset.test.ts asserts html-export's CSS_RESET is byte-identical to the
|
||||
* published head text and that the editor.css block carries every one of
|
||||
* these declarations.
|
||||
*/
|
||||
export const RESET_ALL_DECLS = 'box-sizing:border-box;margin:0;padding:0';
|
||||
export const BODY_BASE_DECLS =
|
||||
"font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;color:#1f2937;-webkit-font-smoothing:antialiased";
|
||||
export const IMG_DECLS = 'max-width:100%;height:auto;display:block';
|
||||
export const LINK_DECLS = 'color:inherit';
|
||||
|
||||
/** Minified published reset (document head). Must stay byte-identical to the backend's `$reset`. */
|
||||
export const CSS_RESET =
|
||||
`*,*::before,*::after{${RESET_ALL_DECLS}}html,body{overflow-x:clip}` +
|
||||
`body{${BODY_BASE_DECLS}}img{${IMG_DECLS}}a{${LINK_DECLS}}`;
|
||||
@@ -1,3 +1,4 @@
|
||||
import { CSS_RESET } from './css-reset';
|
||||
import { componentResolver } from '../components/resolver';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { cssPropsToString } from './style-helpers';
|
||||
@@ -214,7 +215,7 @@ function renderNode(
|
||||
return { html: allChildrenHtml };
|
||||
}
|
||||
|
||||
const CSS_RESET = `*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{overflow-x:clip}body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;color:#1f2937;-webkit-font-smoothing:antialiased}img{max-width:100%;height:auto;display:block}a{color:inherit}`;
|
||||
// CSS_RESET (minified published reset) lives in ./css-reset, shared with the editor canvas.
|
||||
|
||||
const CSS_RESET_PRETTY = `*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -110,8 +110,9 @@ describe('buildCanvasTokenCss', () => {
|
||||
const css = buildCanvasTokenCss(DEFAULT_SITE_DESIGN);
|
||||
expect(css).toContain(
|
||||
'.canvas-device-frame{font-family:var(--wsb-body-font);color:var(--wsb-text);background:var(--wsb-bg)}' +
|
||||
'.canvas-device-frame h1,.canvas-device-frame h2,.canvas-device-frame h3,.canvas-device-frame h4,.canvas-device-frame h5,.canvas-device-frame h6{font-family:var(--wsb-heading-font)}' +
|
||||
'.canvas-device-frame a{color:var(--wsb-link)}',
|
||||
// descendant rules are zero-specificity (:where) like the published `h1..h6{}` / `a{}`
|
||||
':where(.canvas-device-frame) :where(h1,h2,h3,h4,h5,h6){font-family:var(--wsb-heading-font)}' +
|
||||
':where(.canvas-device-frame) :where(a){color:var(--wsb-link)}',
|
||||
);
|
||||
// No unscoped selectors leak into the editor chrome.
|
||||
expect(css).not.toMatch(/(^|\})(body|h1|a)\{/);
|
||||
|
||||
@@ -99,11 +99,15 @@ export function resolveTokenValue(v: string, design: SiteDesign): string {
|
||||
// device frame (not :root) so the editor chrome never inherits site
|
||||
// fonts/colours.
|
||||
const CANVAS_SCOPE = '.canvas-device-frame';
|
||||
// Descendant rules are `:where()`-wrapped (zero specificity) to match the
|
||||
// published `h1..h6{}` / `a{}` rules: with a plain `.canvas-device-frame a`
|
||||
// (0,1,1) a component's own class rule (0,1,0) would lose in the editor but
|
||||
// win when published.
|
||||
const CANVAS_ZERO = `:where(${CANVAS_SCOPE})`;
|
||||
const CANVAS_TOKEN_BASE_CSS =
|
||||
`${CANVAS_SCOPE}{font-family:var(--wsb-body-font);color:var(--wsb-text);background:var(--wsb-bg)}` +
|
||||
['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].map((h) => `${CANVAS_SCOPE} ${h}`).join(',') +
|
||||
`{font-family:var(--wsb-heading-font)}` +
|
||||
`${CANVAS_SCOPE} a{color:var(--wsb-link)}`;
|
||||
`${CANVAS_ZERO} :where(h1,h2,h3,h4,h5,h6){font-family:var(--wsb-heading-font)}` +
|
||||
`${CANVAS_ZERO} :where(a){color:var(--wsb-link)}`;
|
||||
|
||||
/**
|
||||
* Defines the 16 `--wsb-*` variables on the editor canvas frame, plus the
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>About</title>
|
||||
<meta property="og:title" content="About">
|
||||
<meta property="og:type" content="website">
|
||||
<link rel="canonical" href="https://olsdigital.com/about">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{overflow-x:clip}body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;color:#1f2937;-webkit-font-smoothing:antialiased}img{max-width:100%;height:auto;display:block}a{color:inherit}@media(max-width:767px){[style*="display: flex"][style*="flex-direction: row"],[style*="display:flex"][style*="flex-direction:row"]{flex-direction:column!important}}@media(min-width:992px){[data-hide-desktop]{display:none!important}}@media(min-width:768px) and (max-width:991px){[data-hide-tablet]{display:none!important}}@media(max-width:767px){[data-hide-mobile]{display:none!important}}:root{--wsb-primary:#3b82f6;--wsb-secondary:#8b5cf6;--wsb-accent:#10b981;--wsb-link:#3b82f6;--wsb-success:#10b981;--wsb-warning:#f59e0b;--wsb-error:#ef4444;--wsb-bg:#ffffff;--wsb-text:#1f2937;--wsb-muted:#6b7280;--wsb-border:#e5e7eb;--wsb-radius:8px;--wsb-heading-font:Inter, sans-serif;--wsb-body-font:Inter, sans-serif;--wsb-button-font:Inter, sans-serif;--wsb-button-radius:8px}body{font-family:var(--wsb-body-font);color:var(--wsb-text);background:var(--wsb-bg)}h1,h2,h3,h4,h5,h6{font-family:var(--wsb-heading-font)}a{color:var(--wsb-link)}a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--wsb-accent);outline-offset:2px}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
.nav_yk1fl5 .navbar-link:hover { color: #3b82f6 !important; }
|
||||
.nav_yk1fl5 .navbar-cta:hover { filter: brightness(1.1); }
|
||||
</style>
|
||||
<nav class="nav_yk1fl5" style="display:flex;align-items:center;justify-content:space-between;padding:16px 24px;background-color:#0f172a;border-bottom:1px solid #e4e4e7">
|
||||
<a href="/" style="text-decoration:none;display:flex;align-items:center;flex-shrink:0"><img src="assets/1789158005_2592c5cf_Minimalist_OLS_Digital_Logo_with_Rounded_Elements__400_x_135_px_.webp" alt="Logo" style="width:120px;height:auto;display:block" /></a>
|
||||
<div class="navbar-links" id="nav_yk1fl5_links" style="display:flex;align-items:center;gap:24px">
|
||||
<a href="/" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Home</a>
|
||||
<a href="/brands" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Our Brands</a>
|
||||
<a href="/about" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">About</a>
|
||||
<a href="/contact" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
<a href="/contact" class="navbar-cta" style="text-decoration:none;font-size:14px;font-weight:600;color:#ffffff;background-color:#3b82f6;padding:8px 20px;border-radius:6px;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><section id="about" style="background:#1e293b;min-height:35vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px">
|
||||
|
||||
<div style="max-width:800px;width:100%;position:relative;z-index:2;text-align:center">
|
||||
<h1 style="font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2">About OLS Digital</h1>
|
||||
<p style="font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line">Who we are and what we believe in.</p>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap"></div>
|
||||
</div>
|
||||
</section><section id="our-story" style="width:100%;padding-top:60px;padding-bottom:20px"><div style="max-width:1200px;margin:0 auto"><div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><h2 style="font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1">Our Story</h2><p style="font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1">OLS Digital was founded to bring structure, resources, and long-term support to promising digital brands. Rather than absorbing the businesses we work with, we help them keep their independent identity while benefiting from shared operational support.</p></div></div></section><section id="our-mission" style="width:100%;padding-top:20px;padding-bottom:60px"><div style="max-width:1200px;margin:0 auto"><div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><h2 style="font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1">Our Mission</h2><p style="font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1">We believe in honesty, quality, and putting people first. Every brand under the OLS Digital name is expected to uphold these same values in how they serve their customers.</p></div></div></section><section id="about-contact" style="background:linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);padding:80px 20px;text-align:center">
|
||||
<div style="max-width:700px;margin:0 auto">
|
||||
<h2 style="font-size:36px;font-weight:700;color:#ffffff;margin-bottom:12px">Have Questions?</h2>
|
||||
<p style="font-size:18px;color:rgba(255,255,255,0.85);margin-bottom:28px;line-height:1.6">We'd love to hear from you.</p>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap"><a href="mailto:info@olsdigital.com" style="display:inline-block;padding:14px 36px;background-color:var(--wsb-bg);color:var(--wsb-text);text-decoration:none;border-radius:8px;font-weight:600;font-size:16px">Email info@olsdigital.com</a></div>
|
||||
</div>
|
||||
</section></div>
|
||||
<footer style="padding:24px 20px;text-align:center">© 2026 OLS Digital. All rights reserved. Questions? Email info@olsdigital.com</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Brands</title>
|
||||
<meta property="og:title" content="Brands">
|
||||
<meta property="og:type" content="website">
|
||||
<link rel="canonical" href="https://olsdigital.com/brands">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{overflow-x:clip}body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;color:#1f2937;-webkit-font-smoothing:antialiased}img{max-width:100%;height:auto;display:block}a{color:inherit}@media(max-width:767px){[style*="display: flex"][style*="flex-direction: row"],[style*="display:flex"][style*="flex-direction:row"]{flex-direction:column!important}}@media(min-width:992px){[data-hide-desktop]{display:none!important}}@media(min-width:768px) and (max-width:991px){[data-hide-tablet]{display:none!important}}@media(max-width:767px){[data-hide-mobile]{display:none!important}}:root{--wsb-primary:#3b82f6;--wsb-secondary:#8b5cf6;--wsb-accent:#10b981;--wsb-link:#3b82f6;--wsb-success:#10b981;--wsb-warning:#f59e0b;--wsb-error:#ef4444;--wsb-bg:#ffffff;--wsb-text:#1f2937;--wsb-muted:#6b7280;--wsb-border:#e5e7eb;--wsb-radius:8px;--wsb-heading-font:Inter, sans-serif;--wsb-body-font:Inter, sans-serif;--wsb-button-font:Inter, sans-serif;--wsb-button-radius:8px}body{font-family:var(--wsb-body-font);color:var(--wsb-text);background:var(--wsb-bg)}h1,h2,h3,h4,h5,h6{font-family:var(--wsb-heading-font)}a{color:var(--wsb-link)}a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--wsb-accent);outline-offset:2px}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
.nav_yk1fl5 .navbar-link:hover { color: #3b82f6 !important; }
|
||||
.nav_yk1fl5 .navbar-cta:hover { filter: brightness(1.1); }
|
||||
</style>
|
||||
<nav class="nav_yk1fl5" style="display:flex;align-items:center;justify-content:space-between;padding:16px 24px;background-color:#0f172a;border-bottom:1px solid #e4e4e7">
|
||||
<a href="/" style="text-decoration:none;display:flex;align-items:center;flex-shrink:0"><img src="assets/1789158005_2592c5cf_Minimalist_OLS_Digital_Logo_with_Rounded_Elements__400_x_135_px_.webp" alt="Logo" style="width:120px;height:auto;display:block" /></a>
|
||||
<div class="navbar-links" id="nav_yk1fl5_links" style="display:flex;align-items:center;gap:24px">
|
||||
<a href="/" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Home</a>
|
||||
<a href="/brands" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Our Brands</a>
|
||||
<a href="/about" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">About</a>
|
||||
<a href="/contact" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
<a href="/contact" class="navbar-cta" style="text-decoration:none;font-size:14px;font-weight:600;color:#ffffff;background-color:#3b82f6;padding:8px 20px;border-radius:6px;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><section id="brands" style="background:#1e293b;min-height:35vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px">
|
||||
|
||||
<div style="max-width:800px;width:100%;position:relative;z-index:2;text-align:center">
|
||||
<h1 style="font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2">Our Brands</h1>
|
||||
<p style="font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line">A closer look at the companies under the OLS Digital umbrella.</p>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap"></div>
|
||||
</div>
|
||||
</section><style>
|
||||
.cols_67iy9y > :nth-child(1) { flex: 0 0 calc(40% - 40px) !important; }
|
||||
.cols_67iy9y > :nth-child(2) { flex: 0 0 calc(60% - 40px) !important; }
|
||||
</style>
|
||||
<div class="cols_67iy9y" id="anhonesthost" style="display:flex;flex-wrap:wrap;gap:40px;width:100%;padding-top:60px;padding-bottom:20px"><div style="flex:0 0 calc(40% - 40px);min-height:60px;padding:8px"></div><div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><h2 style="font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1">AnHonestHost</h2><p style="font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1">AnHonestHost provides transparent, no-nonsense web hosting for individuals and businesses. Straightforward pricing, honest support, and reliable infrastructure — no surprises.</p></div></div><hr style="border:none;border-top:1px solid #e2e8f0;margin:16px 0" /><style>
|
||||
.cols_1ctfb24 > :nth-child(1) { flex: 0 0 calc(60% - 40px) !important; }
|
||||
.cols_1ctfb24 > :nth-child(2) { flex: 0 0 calc(40% - 40px) !important; }
|
||||
</style>
|
||||
<div class="cols_1ctfb24" id="streamertools" style="display:flex;flex-wrap:wrap;gap:40px;width:100%;padding-top:60px;padding-bottom:60px"><div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><h2 style="font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1">StreamerTools</h2><p style="font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1">StreamerTools builds practical tools and resources for streamers and content creators, helping them grow their channels and streamline their workflow.</p></div><div style="flex:0 0 calc(40% - 40px);min-height:60px;padding:8px"></div></div><section id="brands-contact" style="background:linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);padding:80px 20px;text-align:center">
|
||||
<div style="max-width:700px;margin:0 auto">
|
||||
<h2 style="font-size:36px;font-weight:700;color:#ffffff;margin-bottom:12px">Interested in One of Our Brands?</h2>
|
||||
<p style="font-size:18px;color:rgba(255,255,255,0.85);margin-bottom:28px;line-height:1.6">Reach out and we'll point you in the right direction.</p>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap"><a href="mailto:info@olsdigital.com" style="display:inline-block;padding:14px 36px;background-color:var(--wsb-bg);color:var(--wsb-text);text-decoration:none;border-radius:8px;font-weight:600;font-size:16px">Email info@olsdigital.com</a></div>
|
||||
</div>
|
||||
</section></div>
|
||||
<footer style="padding:24px 20px;text-align:center">© 2026 OLS Digital. All rights reserved. Questions? Email info@olsdigital.com</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Contact</title>
|
||||
<meta property="og:title" content="Contact">
|
||||
<meta property="og:type" content="website">
|
||||
<link rel="canonical" href="https://olsdigital.com/contact">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{overflow-x:clip}body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;color:#1f2937;-webkit-font-smoothing:antialiased}img{max-width:100%;height:auto;display:block}a{color:inherit}@media(max-width:767px){[style*="display: flex"][style*="flex-direction: row"],[style*="display:flex"][style*="flex-direction:row"]{flex-direction:column!important}}@media(min-width:992px){[data-hide-desktop]{display:none!important}}@media(min-width:768px) and (max-width:991px){[data-hide-tablet]{display:none!important}}@media(max-width:767px){[data-hide-mobile]{display:none!important}}:root{--wsb-primary:#3b82f6;--wsb-secondary:#8b5cf6;--wsb-accent:#10b981;--wsb-link:#3b82f6;--wsb-success:#10b981;--wsb-warning:#f59e0b;--wsb-error:#ef4444;--wsb-bg:#ffffff;--wsb-text:#1f2937;--wsb-muted:#6b7280;--wsb-border:#e5e7eb;--wsb-radius:8px;--wsb-heading-font:Inter, sans-serif;--wsb-body-font:Inter, sans-serif;--wsb-button-font:Inter, sans-serif;--wsb-button-radius:8px}body{font-family:var(--wsb-body-font);color:var(--wsb-text);background:var(--wsb-bg)}h1,h2,h3,h4,h5,h6{font-family:var(--wsb-heading-font)}a{color:var(--wsb-link)}a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--wsb-accent);outline-offset:2px}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
.nav_yk1fl5 .navbar-link:hover { color: #3b82f6 !important; }
|
||||
.nav_yk1fl5 .navbar-cta:hover { filter: brightness(1.1); }
|
||||
</style>
|
||||
<nav class="nav_yk1fl5" style="display:flex;align-items:center;justify-content:space-between;padding:16px 24px;background-color:#0f172a;border-bottom:1px solid #e4e4e7">
|
||||
<a href="/" style="text-decoration:none;display:flex;align-items:center;flex-shrink:0"><img src="assets/1789158005_2592c5cf_Minimalist_OLS_Digital_Logo_with_Rounded_Elements__400_x_135_px_.webp" alt="Logo" style="width:120px;height:auto;display:block" /></a>
|
||||
<div class="navbar-links" id="nav_yk1fl5_links" style="display:flex;align-items:center;gap:24px">
|
||||
<a href="/" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Home</a>
|
||||
<a href="/brands" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Our Brands</a>
|
||||
<a href="/about" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">About</a>
|
||||
<a href="/contact" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
<a href="/contact" class="navbar-cta" style="text-decoration:none;font-size:14px;font-weight:600;color:#ffffff;background-color:#3b82f6;padding:8px 20px;border-radius:6px;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><section id="contact" style="background:linear-gradient(135deg, #0f172a, #334155);min-height:40vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px">
|
||||
|
||||
<div style="max-width:800px;width:100%;position:relative;z-index:2;text-align:center">
|
||||
<h1 style="font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2">Contact Us</h1>
|
||||
<p style="font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line">Have a question about OLS Digital or one of our brands? We're happy to help.</p>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap"></div>
|
||||
</div>
|
||||
</section><section id="contact-info" style="width:100%;padding-top:60px;padding-bottom:60px;text-align:center"><div style="max-width:1200px;margin:0 auto"><div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><h2 style="font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1">Get In Touch</h2><p style="font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1">For all inquiries regarding OLS Digital, AnHonestHost, StreamerTools, or any of our other brands, please reach out via email and our team will respond as soon as possible.</p><div style="height:20px"></div><a href="mailto:info@olsdigital.com" style="display:inline-block;text-decoration:none;background-color:#3b82f6;color:#ffffff;padding:12px 24px;border-radius:8px;font-weight:600;font-size:16px;border:none;box-shadow:none;opacity:1">info@olsdigital.com</a></div></div></section></div>
|
||||
<footer style="padding:24px 20px;text-align:center">© 2026 OLS Digital. All rights reserved. Questions? Email info@olsdigital.com</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Home</title>
|
||||
<meta property="og:title" content="Home">
|
||||
<meta property="og:type" content="website">
|
||||
<link rel="canonical" href="https://olsdigital.com/">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}html,body{overflow-x:clip}body{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;line-height:1.6;color:#1f2937;-webkit-font-smoothing:antialiased}img{max-width:100%;height:auto;display:block}a{color:inherit}@media(max-width:767px){[style*="display: flex"][style*="flex-direction: row"],[style*="display:flex"][style*="flex-direction:row"]{flex-direction:column!important}}@media(min-width:992px){[data-hide-desktop]{display:none!important}}@media(min-width:768px) and (max-width:991px){[data-hide-tablet]{display:none!important}}@media(max-width:767px){[data-hide-mobile]{display:none!important}}:root{--wsb-primary:#3b82f6;--wsb-secondary:#8b5cf6;--wsb-accent:#10b981;--wsb-link:#3b82f6;--wsb-success:#10b981;--wsb-warning:#f59e0b;--wsb-error:#ef4444;--wsb-bg:#ffffff;--wsb-text:#1f2937;--wsb-muted:#6b7280;--wsb-border:#e5e7eb;--wsb-radius:8px;--wsb-heading-font:Inter, sans-serif;--wsb-body-font:Inter, sans-serif;--wsb-button-font:Inter, sans-serif;--wsb-button-radius:8px}body{font-family:var(--wsb-body-font);color:var(--wsb-text);background:var(--wsb-bg)}h1,h2,h3,h4,h5,h6{font-family:var(--wsb-heading-font)}a{color:var(--wsb-link)}a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--wsb-accent);outline-offset:2px}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important;scroll-behavior:auto!important}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
.nav_yk1fl5 .navbar-link:hover { color: #3b82f6 !important; }
|
||||
.nav_yk1fl5 .navbar-cta:hover { filter: brightness(1.1); }
|
||||
</style>
|
||||
<nav class="nav_yk1fl5" style="display:flex;align-items:center;justify-content:space-between;padding:16px 24px;background-color:#0f172a;border-bottom:1px solid #e4e4e7">
|
||||
<a href="/" style="text-decoration:none;display:flex;align-items:center;flex-shrink:0"><img src="assets/1789158005_2592c5cf_Minimalist_OLS_Digital_Logo_with_Rounded_Elements__400_x_135_px_.webp" alt="Logo" style="width:120px;height:auto;display:block" /></a>
|
||||
<div class="navbar-links" id="nav_yk1fl5_links" style="display:flex;align-items:center;gap:24px">
|
||||
<a href="/" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Home</a>
|
||||
<a href="/brands" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Our Brands</a>
|
||||
<a href="/about" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">About</a>
|
||||
<a href="/contact" class="navbar-link" style="text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
<a href="/contact" class="navbar-cta" style="text-decoration:none;font-size:14px;font-weight:600;color:#ffffff;background-color:#3b82f6;padding:8px 20px;border-radius:6px;transition:color 0.15s, background-color 0.15s">Contact</a>
|
||||
</div>
|
||||
</nav>
|
||||
<div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><section id="home" style="background:linear-gradient(135deg, #0f172a, #334155);min-height:60vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px">
|
||||
|
||||
<div style="max-width:800px;width:100%;position:relative;z-index:2;text-align:center">
|
||||
<h1 style="font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2">OLS Digital</h1>
|
||||
<p style="font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line">The parent company behind a growing family of trusted online brands, including AnHonestHost and StreamerTools.</p>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap"><a href="/brands" style="display:inline-block;padding:14px 36px;background-color:#3b82f6;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;font-size:16px">Explore Our Brands</a><a href="/contact" style="display:inline-block;padding:14px 36px;background-color:transparent;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;font-size:16px;border:2px solid #ffffff">Contact Us</a></div>
|
||||
</div>
|
||||
</section><section id="what-we-do" style="padding:80px 20px;background-color:#ffffff">
|
||||
<div style="max-width:1100px;margin:0 auto;display:grid;grid-template-columns:repeat(3,1fr);gap:32px">
|
||||
<div style="text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)">
|
||||
<div style="font-size:36px;margin-bottom:16px">🏢</div>
|
||||
<h3 style="font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px">Multi-Brand Portfolio</h3>
|
||||
<p style="font-size:14px;color:var(--wsb-muted);line-height:1.6">We own and support a growing collection of independent, high-quality brands.</p>
|
||||
</div>
|
||||
<div style="text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)">
|
||||
<div style="font-size:36px;margin-bottom:16px">🌐</div>
|
||||
<h3 style="font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px">AnHonestHost</h3>
|
||||
<p style="font-size:14px;color:var(--wsb-muted);line-height:1.6">Transparent, reliable web hosting built on trust.</p>
|
||||
</div>
|
||||
<div style="text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)">
|
||||
<div style="font-size:36px;margin-bottom:16px">🎮</div>
|
||||
<h3 style="font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px">StreamerTools</h3>
|
||||
<p style="font-size:14px;color:var(--wsb-muted);line-height:1.6">Tools and resources built for content creators and streamers.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section><section id="about-preview" style="width:100%;padding-top:60px;padding-bottom:60px"><div style="max-width:1200px;margin:0 auto"><div style="padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1"><h2 style="font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1">Building Brands That Matter</h2><p style="font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1">OLS Digital is a privately held parent company focused on acquiring and growing digital-first businesses. Each brand under our umbrella operates independently, guided by a shared commitment to honesty, quality, and great customer experiences.</p></div></div></section><section id="contact-cta" style="background:linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);padding:80px 20px;text-align:center">
|
||||
<div style="max-width:700px;margin:0 auto">
|
||||
<h2 style="font-size:36px;font-weight:700;color:#ffffff;margin-bottom:12px">Want to Learn More?</h2>
|
||||
<p style="font-size:18px;color:rgba(255,255,255,0.85);margin-bottom:28px;line-height:1.6">For inquiries about OLS Digital or any of our brands, reach out to our team directly.</p>
|
||||
<div style="display:flex;gap:12px;justify-content:center;flex-wrap:wrap"><a href="mailto:info@olsdigital.com" style="display:inline-block;padding:14px 36px;background-color:var(--wsb-bg);color:var(--wsb-text);text-decoration:none;border-radius:8px;font-weight:600;font-size:16px">Email info@olsdigital.com</a></div>
|
||||
</div>
|
||||
</section></div>
|
||||
<footer style="padding:24px 20px;text-align:center">© 2026 OLS Digital. All rights reserved. Questions? Email info@olsdigital.com</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"site_id": 100,
|
||||
"name": "OneLastShot Digital",
|
||||
"html": "<div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><section id=\"home\" style=\"background:linear-gradient(135deg, #0f172a, #334155);min-height:60vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px\">\n\n<div style=\"max-width:800px;width:100%;position:relative;z-index:2;text-align:center\">\n <h1 style=\"font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2\">OLS Digital</h1>\n <p style=\"font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line\">The parent company behind a growing family of trusted online brands, including AnHonestHost and StreamerTools.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"><a href=\"/brands\" style=\"display:inline-block;padding:14px 36px;background-color:#3b82f6;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;font-size:16px\">Explore Our Brands</a><a href=\"/contact\" style=\"display:inline-block;padding:14px 36px;background-color:transparent;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;font-size:16px;border:2px solid #ffffff\">Contact Us</a></div>\n</div>\n</section><section id=\"what-we-do\" style=\"padding:80px 20px;background-color:#ffffff\">\n <div style=\"max-width:1100px;margin:0 auto;display:grid;grid-template-columns:repeat(3,1fr);gap:32px\">\n <div style=\"text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)\">\n <div style=\"font-size:36px;margin-bottom:16px\">\ud83c\udfe2</div>\n <h3 style=\"font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px\">Multi-Brand Portfolio</h3>\n <p style=\"font-size:14px;color:var(--wsb-muted);line-height:1.6\">We own and support a growing collection of independent, high-quality brands.</p>\n </div>\n <div style=\"text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)\">\n <div style=\"font-size:36px;margin-bottom:16px\">\ud83c\udf10</div>\n <h3 style=\"font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px\">AnHonestHost</h3>\n <p style=\"font-size:14px;color:var(--wsb-muted);line-height:1.6\">Transparent, reliable web hosting built on trust.</p>\n </div>\n <div style=\"text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)\">\n <div style=\"font-size:36px;margin-bottom:16px\">\ud83c\udfae</div>\n <h3 style=\"font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px\">StreamerTools</h3>\n <p style=\"font-size:14px;color:var(--wsb-muted);line-height:1.6\">Tools and resources built for content creators and streamers.</p>\n </div>\n </div>\n</section><section id=\"about-preview\" style=\"width:100%;padding-top:60px;padding-bottom:60px\"><div style=\"max-width:1200px;margin:0 auto\"><div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><h2 style=\"font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1\">Building Brands That Matter</h2><p style=\"font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1\">OLS Digital is a privately held parent company focused on acquiring and growing digital-first businesses. Each brand under our umbrella operates independently, guided by a shared commitment to honesty, quality, and great customer experiences.</p></div></div></section><section id=\"contact-cta\" style=\"background:linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);padding:80px 20px;text-align:center\">\n <div style=\"max-width:700px;margin:0 auto\">\n <h2 style=\"font-size:36px;font-weight:700;color:#ffffff;margin-bottom:12px\">Want to Learn More?</h2>\n <p style=\"font-size:18px;color:rgba(255,255,255,0.85);margin-bottom:28px;line-height:1.6\">For inquiries about OLS Digital or any of our brands, reach out to our team directly.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"><a href=\"mailto:info@olsdigital.com\" style=\"display:inline-block;padding:14px 36px;background-color:var(--wsb-bg);color:var(--wsb-text);text-decoration:none;border-radius:8px;font-weight:600;font-size:16px\">Email info@olsdigital.com</a></div>\n </div>\n</section></div>",
|
||||
"css": "",
|
||||
"pages": [
|
||||
{
|
||||
"filename": "index.html",
|
||||
"title": "Home",
|
||||
"html": "<div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><section id=\"home\" style=\"background:linear-gradient(135deg, #0f172a, #334155);min-height:60vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px\">\n\n<div style=\"max-width:800px;width:100%;position:relative;z-index:2;text-align:center\">\n <h1 style=\"font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2\">OLS Digital</h1>\n <p style=\"font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line\">The parent company behind a growing family of trusted online brands, including AnHonestHost and StreamerTools.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"><a href=\"/brands\" style=\"display:inline-block;padding:14px 36px;background-color:#3b82f6;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;font-size:16px\">Explore Our Brands</a><a href=\"/contact\" style=\"display:inline-block;padding:14px 36px;background-color:transparent;color:#ffffff;text-decoration:none;border-radius:8px;font-weight:600;font-size:16px;border:2px solid #ffffff\">Contact Us</a></div>\n</div>\n</section><section id=\"what-we-do\" style=\"padding:80px 20px;background-color:#ffffff\">\n <div style=\"max-width:1100px;margin:0 auto;display:grid;grid-template-columns:repeat(3,1fr);gap:32px\">\n <div style=\"text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)\">\n <div style=\"font-size:36px;margin-bottom:16px\">\ud83c\udfe2</div>\n <h3 style=\"font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px\">Multi-Brand Portfolio</h3>\n <p style=\"font-size:14px;color:var(--wsb-muted);line-height:1.6\">We own and support a growing collection of independent, high-quality brands.</p>\n </div>\n <div style=\"text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)\">\n <div style=\"font-size:36px;margin-bottom:16px\">\ud83c\udf10</div>\n <h3 style=\"font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px\">AnHonestHost</h3>\n <p style=\"font-size:14px;color:var(--wsb-muted);line-height:1.6\">Transparent, reliable web hosting built on trust.</p>\n </div>\n <div style=\"text-align:center;padding:32px 24px;border-radius:12px;background-color:var(--wsb-bg);border:1px solid var(--wsb-border)\">\n <div style=\"font-size:36px;margin-bottom:16px\">\ud83c\udfae</div>\n <h3 style=\"font-size:20px;font-weight:600;color:var(--wsb-text);margin-bottom:8px\">StreamerTools</h3>\n <p style=\"font-size:14px;color:var(--wsb-muted);line-height:1.6\">Tools and resources built for content creators and streamers.</p>\n </div>\n </div>\n</section><section id=\"about-preview\" style=\"width:100%;padding-top:60px;padding-bottom:60px\"><div style=\"max-width:1200px;margin:0 auto\"><div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><h2 style=\"font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1\">Building Brands That Matter</h2><p style=\"font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1\">OLS Digital is a privately held parent company focused on acquiring and growing digital-first businesses. Each brand under our umbrella operates independently, guided by a shared commitment to honesty, quality, and great customer experiences.</p></div></div></section><section id=\"contact-cta\" style=\"background:linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);padding:80px 20px;text-align:center\">\n <div style=\"max-width:700px;margin:0 auto\">\n <h2 style=\"font-size:36px;font-weight:700;color:#ffffff;margin-bottom:12px\">Want to Learn More?</h2>\n <p style=\"font-size:18px;color:rgba(255,255,255,0.85);margin-bottom:28px;line-height:1.6\">For inquiries about OLS Digital or any of our brands, reach out to our team directly.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"><a href=\"mailto:info@olsdigital.com\" style=\"display:inline-block;padding:14px 36px;background-color:var(--wsb-bg);color:var(--wsb-text);text-decoration:none;border-radius:8px;font-weight:600;font-size:16px\">Email info@olsdigital.com</a></div>\n </div>\n</section></div>"
|
||||
},
|
||||
{
|
||||
"filename": "brands.html",
|
||||
"title": "Brands",
|
||||
"html": "<div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><section id=\"brands\" style=\"background:#1e293b;min-height:35vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px\">\n\n<div style=\"max-width:800px;width:100%;position:relative;z-index:2;text-align:center\">\n <h1 style=\"font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2\">Our Brands</h1>\n <p style=\"font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line\">A closer look at the companies under the OLS Digital umbrella.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"></div>\n</div>\n</section><style>\n .cols_67iy9y > :nth-child(1) { flex: 0 0 calc(40% - 40px) !important; }\n .cols_67iy9y > :nth-child(2) { flex: 0 0 calc(60% - 40px) !important; }\n</style>\n<div class=\"cols_67iy9y\" id=\"anhonesthost\" style=\"display:flex;flex-wrap:wrap;gap:40px;width:100%;padding-top:60px;padding-bottom:20px\"><div style=\"flex:0 0 calc(40% - 40px);min-height:60px;padding:8px\"></div><div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><h2 style=\"font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1\">AnHonestHost</h2><p style=\"font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1\">AnHonestHost provides transparent, no-nonsense web hosting for individuals and businesses. Straightforward pricing, honest support, and reliable infrastructure \u2014 no surprises.</p></div></div><hr style=\"border:none;border-top:1px solid #e2e8f0;margin:16px 0\" /><style>\n .cols_1ctfb24 > :nth-child(1) { flex: 0 0 calc(60% - 40px) !important; }\n .cols_1ctfb24 > :nth-child(2) { flex: 0 0 calc(40% - 40px) !important; }\n</style>\n<div class=\"cols_1ctfb24\" id=\"streamertools\" style=\"display:flex;flex-wrap:wrap;gap:40px;width:100%;padding-top:60px;padding-bottom:60px\"><div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><h2 style=\"font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1\">StreamerTools</h2><p style=\"font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1\">StreamerTools builds practical tools and resources for streamers and content creators, helping them grow their channels and streamline their workflow.</p></div><div style=\"flex:0 0 calc(40% - 40px);min-height:60px;padding:8px\"></div></div><section id=\"brands-contact\" style=\"background:linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);padding:80px 20px;text-align:center\">\n <div style=\"max-width:700px;margin:0 auto\">\n <h2 style=\"font-size:36px;font-weight:700;color:#ffffff;margin-bottom:12px\">Interested in One of Our Brands?</h2>\n <p style=\"font-size:18px;color:rgba(255,255,255,0.85);margin-bottom:28px;line-height:1.6\">Reach out and we'll point you in the right direction.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"><a href=\"mailto:info@olsdigital.com\" style=\"display:inline-block;padding:14px 36px;background-color:var(--wsb-bg);color:var(--wsb-text);text-decoration:none;border-radius:8px;font-weight:600;font-size:16px\">Email info@olsdigital.com</a></div>\n </div>\n</section></div>"
|
||||
},
|
||||
{
|
||||
"filename": "about.html",
|
||||
"title": "About",
|
||||
"html": "<div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><section id=\"about\" style=\"background:#1e293b;min-height:35vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px\">\n\n<div style=\"max-width:800px;width:100%;position:relative;z-index:2;text-align:center\">\n <h1 style=\"font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2\">About OLS Digital</h1>\n <p style=\"font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line\">Who we are and what we believe in.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"></div>\n</div>\n</section><section id=\"our-story\" style=\"width:100%;padding-top:60px;padding-bottom:20px\"><div style=\"max-width:1200px;margin:0 auto\"><div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><h2 style=\"font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1\">Our Story</h2><p style=\"font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1\">OLS Digital was founded to bring structure, resources, and long-term support to promising digital brands. Rather than absorbing the businesses we work with, we help them keep their independent identity while benefiting from shared operational support.</p></div></div></section><section id=\"our-mission\" style=\"width:100%;padding-top:20px;padding-bottom:60px\"><div style=\"max-width:1200px;margin:0 auto\"><div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><h2 style=\"font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1\">Our Mission</h2><p style=\"font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1\">We believe in honesty, quality, and putting people first. Every brand under the OLS Digital name is expected to uphold these same values in how they serve their customers.</p></div></div></section><section id=\"about-contact\" style=\"background:linear-gradient(135deg, #2563eb 0%, #7c3aed 100%);padding:80px 20px;text-align:center\">\n <div style=\"max-width:700px;margin:0 auto\">\n <h2 style=\"font-size:36px;font-weight:700;color:#ffffff;margin-bottom:12px\">Have Questions?</h2>\n <p style=\"font-size:18px;color:rgba(255,255,255,0.85);margin-bottom:28px;line-height:1.6\">We'd love to hear from you.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"><a href=\"mailto:info@olsdigital.com\" style=\"display:inline-block;padding:14px 36px;background-color:var(--wsb-bg);color:var(--wsb-text);text-decoration:none;border-radius:8px;font-weight:600;font-size:16px\">Email info@olsdigital.com</a></div>\n </div>\n</section></div>"
|
||||
},
|
||||
{
|
||||
"filename": "contact.html",
|
||||
"title": "Contact",
|
||||
"html": "<div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><section id=\"contact\" style=\"background:linear-gradient(135deg, #0f172a, #334155);min-height:40vh;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden;padding:60px 20px\">\n\n<div style=\"max-width:800px;width:100%;position:relative;z-index:2;text-align:center\">\n <h1 style=\"font-size:48px;font-weight:700;color:#ffffff;margin-bottom:16px;line-height:1.2\">Contact Us</h1>\n <p style=\"font-size:20px;color:#ffffff;opacity:0.85;margin-bottom:32px;line-height:1.6;white-space:pre-line\">Have a question about OLS Digital or one of our brands? We're happy to help.</p>\n <div style=\"display:flex;gap:12px;justify-content:center;flex-wrap:wrap\"></div>\n</div>\n</section><section id=\"contact-info\" style=\"width:100%;padding-top:60px;padding-bottom:60px;text-align:center\"><div style=\"max-width:1200px;margin:0 auto\"><div style=\"padding:20px;min-height:100px;border:none;box-shadow:none;opacity:1\"><h2 style=\"font-size:36px;font-weight:700;font-family:Inter, sans-serif;color:#1f2937;margin-bottom:16px;border:none;box-shadow:none;opacity:1\">Get In Touch</h2><p style=\"font-size:16px;line-height:1.6;color:#3f3f46;border:none;box-shadow:none;opacity:1\">For all inquiries regarding OLS Digital, AnHonestHost, StreamerTools, or any of our other brands, please reach out via email and our team will respond as soon as possible.</p><div style=\"height:20px\"></div><a href=\"mailto:info@olsdigital.com\" style=\"display:inline-block;text-decoration:none;background-color:#3b82f6;color:#ffffff;padding:12px 24px;border-radius:8px;font-weight:600;font-size:16px;border:none;box-shadow:none;opacity:1\">info@olsdigital.com</a></div></div></section></div>"
|
||||
}
|
||||
],
|
||||
"header_html": "<style>\n .nav_yk1fl5 .navbar-link:hover { color: #3b82f6 !important; }\n .nav_yk1fl5 .navbar-cta:hover { filter: brightness(1.1); }\n</style>\n<nav class=\"nav_yk1fl5\" style=\"display:flex;align-items:center;justify-content:space-between;padding:16px 24px;background-color:#0f172a;border-bottom:1px solid #e4e4e7\">\n <a href=\"/\" style=\"text-decoration:none;display:flex;align-items:center;flex-shrink:0\"><img src=\"/api/assets-proxy/?site_id=100&filename=1789158005_2592c5cf_Minimalist_OLS_Digital_Logo_with_Rounded_Elements__400_x_135_px_.webp\" alt=\"Logo\" style=\"width:120px;height:auto;display:block\" /></a>\n <div class=\"navbar-links\" id=\"nav_yk1fl5_links\" style=\"display:flex;align-items:center;gap:24px\">\n <a href=\"/\" class=\"navbar-link\" style=\"text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s\">Home</a>\n <a href=\"/brands\" class=\"navbar-link\" style=\"text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s\">Our Brands</a>\n <a href=\"/about\" class=\"navbar-link\" style=\"text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s\">About</a>\n <a href=\"/contact\" class=\"navbar-link\" style=\"text-decoration:none;font-size:14px;font-weight:400;color:#ffffff;background-color:transparent;padding:0;border-radius:0;transition:color 0.15s, background-color 0.15s\">Contact</a>\n <a href=\"/contact\" class=\"navbar-cta\" style=\"text-decoration:none;font-size:14px;font-weight:600;color:#ffffff;background-color:#3b82f6;padding:8px 20px;border-radius:6px;transition:color 0.15s, background-color 0.15s\">Contact</a>\n </div>\n</nav>",
|
||||
"footer_html": "<footer style=\"padding:24px 20px;text-align:center\">\u00a9 2026 OLS Digital. All rights reserved. Questions? Email info@olsdigital.com</footer>",
|
||||
"craft_state": "{\"ai-hero-1\":{\"type\":{\"resolvedName\":\"HeroSimple\"},\"isCanvas\":false,\"props\":{\"heading\":\"OLS Digital\",\"subtitle\":\"The parent company behind a growing family of trusted online brands, including AnHonestHost and StreamerTools.\",\"ctas\":[{\"text\":\"Explore Our Brands\",\"href\":\"/brands\",\"variant\":\"primary\"},{\"text\":\"Contact Us\",\"href\":\"/contact\",\"variant\":\"outline\"}],\"bgType\":\"gradient\",\"bgColor\":\"#1e293b\",\"bgGradientFrom\":\"#0f172a\",\"bgGradientTo\":\"#334155\",\"bgGradientAngle\":135,\"bgImage\":\"\",\"bgPosition\":\"\",\"bgVideo\":\"\",\"overlayColor\":\"#000000\",\"overlayOpacity\":0,\"textColor\":\"#ffffff\",\"buttonBgColor\":\"#3b82f6\",\"buttonTextColor\":\"#ffffff\",\"minHeight\":\"60vh\",\"verticalAlign\":\"center\",\"textAlign\":\"center\",\"anchorId\":\"home\",\"style\":{},\"aiName\":\"Hero\",\"node_id\":\"ai-hero-1\"},\"displayName\":\"Hero\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-features-1\":{\"type\":{\"resolvedName\":\"FeaturesGrid\"},\"isCanvas\":false,\"props\":{\"features\":[{\"icon\":\"\ud83c\udfe2\",\"title\":\"Multi-Brand Portfolio\",\"description\":\"We own and support a growing collection of independent, high-quality brands.\"},{\"icon\":\"\ud83c\udf10\",\"title\":\"AnHonestHost\",\"description\":\"Transparent, reliable web hosting built on trust.\"},{\"icon\":\"\ud83c\udfae\",\"title\":\"StreamerTools\",\"description\":\"Tools and resources built for content creators and streamers.\"}],\"style\":{\"backgroundColor\":\"#ffffff\"},\"anchorId\":\"what-we-do\",\"animation\":\"\",\"animationDelay\":\"\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"What We Do\",\"node_id\":\"ai-features-1\"},\"displayName\":\"Features Grid\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-intro-1\":{\"type\":{\"resolvedName\":\"Section\"},\"isCanvas\":false,\"props\":{\"style\":{\"paddingTop\":\"60px\",\"paddingBottom\":\"60px\"},\"innerMaxWidth\":\"1200px\",\"topDivider\":\"none\",\"topDividerColor\":\"#ffffff\",\"topDividerHeight\":\"50px\",\"bottomDivider\":\"none\",\"bottomDividerColor\":\"#ffffff\",\"bottomDividerHeight\":\"50px\",\"anchorId\":\"about-preview\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Intro Section\",\"node_id\":\"ai-section-intro-1\"},\"displayName\":\"Section\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{\"section-inner\":\"ai-section-intro-1__section-inner\"}},\"ai-heading-intro-1\":{\"type\":{\"resolvedName\":\"Heading\"},\"isCanvas\":false,\"props\":{\"text\":\"Building Brands That Matter\",\"html\":\"\",\"level\":\"h2\",\"style\":{\"fontSize\":\"36px\",\"fontWeight\":\"700\",\"fontFamily\":\"Inter, sans-serif\",\"color\":\"#1f2937\",\"marginBottom\":\"16px\",\"lineHeight\":\"\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Intro Heading\",\"node_id\":\"ai-heading-intro-1\"},\"displayName\":\"Heading\",\"custom\":{},\"parent\":\"ai-section-intro-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-text-intro-1\":{\"type\":{\"resolvedName\":\"TextBlock\"},\"isCanvas\":false,\"props\":{\"text\":\"OLS Digital is a privately held parent company focused on acquiring and growing digital-first businesses. Each brand under our umbrella operates independently, guided by a shared commitment to honesty, quality, and great customer experiences.\",\"html\":\"\",\"style\":{\"fontSize\":\"16px\",\"lineHeight\":\"1.6\",\"color\":\"#3f3f46\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Intro Text\",\"node_id\":\"ai-text-intro-1\"},\"displayName\":\"Text\",\"custom\":{},\"parent\":\"ai-section-intro-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-intro-1__section-inner\":{\"type\":{\"resolvedName\":\"Container\"},\"isCanvas\":true,\"props\":{\"style\":{\"padding\":\"20px\",\"minHeight\":\"100px\",\"justifyContent\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottoLine truncated
|
||||
"header_craft_state": "{\"ROOT\":{\"type\":{\"resolvedName\":\"Navbar\"},\"isCanvas\":true,\"props\":{\"logoType\":\"image\",\"logoText\":\"\",\"logoImage\":\"/api/assets-proxy/?site_id=100&filename=1789158005_2592c5cf_Minimalist_OLS_Digital_Logo_with_Rounded_Elements__400_x_135_px_.webp\",\"logoWidth\":\"120px\",\"logoUrl\":\"/\",\"logoFontFamily\":\"Inter, sans-serif\",\"logoFontSize\":\"20px\",\"links\":[{\"text\":\"Home\",\"href\":\"/\",\"source\":\"page\",\"pageId\":\"home\"},{\"text\":\"Our Brands\",\"href\":\"/brands\",\"source\":\"page\",\"pageId\":\"page_mtxec41h_1\"},{\"text\":\"About\",\"href\":\"/about\",\"source\":\"page\",\"pageId\":\"page_mtxec41h_2\"},{\"text\":\"Contact\",\"href\":\"/contact\",\"source\":\"page\",\"pageId\":\"page_mtxec41h_3\"},{\"text\":\"Contact\",\"href\":\"/contact\",\"isCta\":true}],\"followPages\":true,\"backgroundColor\":\"#0f172a\",\"textColor\":\"#ffffff\",\"hoverColor\":\"#3b82f6\",\"ctaColor\":\"#3b82f6\",\"ctaTextColor\":\"#ffffff\",\"padding\":\"16px 24px\",\"navAlignment\":\"space-between\",\"isSticky\":false,\"showMobileMenu\":false,\"style\":{\"borderBottom\":\"1px solid #e4e4e7\"},\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"animation\":\"none\",\"animationDelay\":\"0\",\"aiName\":\"Site Nav\",\"node_id\":\"ai-nav-1\"},\"displayName\":\"Navbar\",\"custom\":{},\"parent\":null,\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}}}",
|
||||
"footer_craft_state": "{\"ROOT\":{\"type\":{\"resolvedName\":\"Footer\"},\"isCanvas\":true,\"props\":{\"aiName\":\"Site Footer\",\"node_id\":\"ai-footer-1\",\"text\":\"\u00a9 2026 OLS Digital. All rights reserved. Questions? Email info@olsdigital.com\"},\"displayName\":\"Footer\",\"custom\":{},\"hidden\":false,\"parent\":null,\"nodes\":[],\"linkedNodes\":{}}}",
|
||||
"pages_craft_state": [
|
||||
{
|
||||
"id": "home",
|
||||
"name": "Home",
|
||||
"slug": "index",
|
||||
"craftState": "{\"ai-hero-1\":{\"type\":{\"resolvedName\":\"HeroSimple\"},\"isCanvas\":false,\"props\":{\"heading\":\"OLS Digital\",\"subtitle\":\"The parent company behind a growing family of trusted online brands, including AnHonestHost and StreamerTools.\",\"ctas\":[{\"text\":\"Explore Our Brands\",\"href\":\"/brands\",\"variant\":\"primary\"},{\"text\":\"Contact Us\",\"href\":\"/contact\",\"variant\":\"outline\"}],\"bgType\":\"gradient\",\"bgColor\":\"#1e293b\",\"bgGradientFrom\":\"#0f172a\",\"bgGradientTo\":\"#334155\",\"bgGradientAngle\":135,\"bgImage\":\"\",\"bgPosition\":\"\",\"bgVideo\":\"\",\"overlayColor\":\"#000000\",\"overlayOpacity\":0,\"textColor\":\"#ffffff\",\"buttonBgColor\":\"#3b82f6\",\"buttonTextColor\":\"#ffffff\",\"minHeight\":\"60vh\",\"verticalAlign\":\"center\",\"textAlign\":\"center\",\"anchorId\":\"home\",\"style\":{},\"aiName\":\"Hero\",\"node_id\":\"ai-hero-1\"},\"displayName\":\"Hero\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-features-1\":{\"type\":{\"resolvedName\":\"FeaturesGrid\"},\"isCanvas\":false,\"props\":{\"features\":[{\"icon\":\"\ud83c\udfe2\",\"title\":\"Multi-Brand Portfolio\",\"description\":\"We own and support a growing collection of independent, high-quality brands.\"},{\"icon\":\"\ud83c\udf10\",\"title\":\"AnHonestHost\",\"description\":\"Transparent, reliable web hosting built on trust.\"},{\"icon\":\"\ud83c\udfae\",\"title\":\"StreamerTools\",\"description\":\"Tools and resources built for content creators and streamers.\"}],\"style\":{\"backgroundColor\":\"#ffffff\"},\"anchorId\":\"what-we-do\",\"animation\":\"\",\"animationDelay\":\"\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"What We Do\",\"node_id\":\"ai-features-1\"},\"displayName\":\"Features Grid\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-intro-1\":{\"type\":{\"resolvedName\":\"Section\"},\"isCanvas\":false,\"props\":{\"style\":{\"paddingTop\":\"60px\",\"paddingBottom\":\"60px\"},\"innerMaxWidth\":\"1200px\",\"topDivider\":\"none\",\"topDividerColor\":\"#ffffff\",\"topDividerHeight\":\"50px\",\"bottomDivider\":\"none\",\"bottomDividerColor\":\"#ffffff\",\"bottomDividerHeight\":\"50px\",\"anchorId\":\"about-preview\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Intro Section\",\"node_id\":\"ai-section-intro-1\"},\"displayName\":\"Section\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{\"section-inner\":\"ai-section-intro-1__section-inner\"}},\"ai-heading-intro-1\":{\"type\":{\"resolvedName\":\"Heading\"},\"isCanvas\":false,\"props\":{\"text\":\"Building Brands That Matter\",\"html\":\"\",\"level\":\"h2\",\"style\":{\"fontSize\":\"36px\",\"fontWeight\":\"700\",\"fontFamily\":\"Inter, sans-serif\",\"color\":\"#1f2937\",\"marginBottom\":\"16px\",\"lineHeight\":\"\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Intro Heading\",\"node_id\":\"ai-heading-intro-1\"},\"displayName\":\"Heading\",\"custom\":{},\"parent\":\"ai-section-intro-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-text-intro-1\":{\"type\":{\"resolvedName\":\"TextBlock\"},\"isCanvas\":false,\"props\":{\"text\":\"OLS Digital is a privately held parent company focused on acquiring and growing digital-first businesses. Each brand under our umbrella operates independently, guided by a shared commitment to honesty, quality, and great customer experiences.\",\"html\":\"\",\"style\":{\"fontSize\":\"16px\",\"lineHeight\":\"1.6\",\"color\":\"#3f3f46\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Intro Text\",\"node_id\":\"ai-text-intro-1\"},\"displayName\":\"Text\",\"custom\":{},\"parent\":\"ai-section-intro-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-intro-1__section-inner\":{\"type\":{\"resolvedName\":\"Container\"},\"isCanvas\":true,\"props\":{\"style\":{\"padding\":\"20px\",\"minHeight\":\"100px\",\"justifyContent\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddiLine truncated
|
||||
},
|
||||
{
|
||||
"id": "page_mtxec41h_1",
|
||||
"name": "Brands",
|
||||
"slug": "brands",
|
||||
"craftState": "{\"ai-hero-brands-1\":{\"type\":{\"resolvedName\":\"HeroSimple\"},\"isCanvas\":false,\"props\":{\"heading\":\"Our Brands\",\"subtitle\":\"A closer look at the companies under the OLS Digital umbrella.\",\"ctas\":[],\"bgType\":\"color\",\"bgColor\":\"#1e293b\",\"bgGradientFrom\":\"#667eea\",\"bgGradientTo\":\"#764ba2\",\"bgGradientAngle\":135,\"bgImage\":\"\",\"bgPosition\":\"\",\"bgVideo\":\"\",\"overlayColor\":\"#000000\",\"overlayOpacity\":0,\"textColor\":\"#ffffff\",\"buttonBgColor\":\"#3b82f6\",\"buttonTextColor\":\"#ffffff\",\"minHeight\":\"35vh\",\"verticalAlign\":\"center\",\"textAlign\":\"center\",\"anchorId\":\"brands\",\"style\":{},\"aiName\":\"Brands Hero\",\"node_id\":\"ai-hero-brands-1\"},\"displayName\":\"Hero\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-columns-anhonesthost-1\":{\"type\":{\"resolvedName\":\"ColumnLayout\"},\"isCanvas\":false,\"props\":{\"columns\":2,\"split\":\"40-60\",\"gap\":\"40px\",\"style\":{\"paddingTop\":\"60px\",\"paddingBottom\":\"20px\"},\"anchorId\":\"anhonesthost\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Brand: AnHonestHost\",\"node_id\":\"ai-columns-anhonesthost-1\"},\"displayName\":\"Columns\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{\"col-0\":\"K8SJDt59Ct\",\"col-1\":\"ai-container-anhonesthost-1\"}},\"ai-container-anhonesthost-1\":{\"type\":{\"resolvedName\":\"Container\"},\"isCanvas\":true,\"props\":{\"style\":{\"padding\":\"20px\",\"minHeight\":\"100px\",\"justifyContent\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"tag\":\"div\",\"fullWidth\":false,\"contentWidth\":\"full\",\"anchorId\":\"\",\"cssId\":\"\",\"cssClass\":\"\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"AnHonestHost Text\",\"node_id\":\"ai-container-anhonesthost-1\"},\"displayName\":\"Container\",\"custom\":{},\"parent\":\"ai-columns-anhonesthost-1\",\"hidden\":false,\"nodes\":[\"ai-heading-anhonesthost-1\",\"ai-text-anhonesthost-1\"],\"linkedNodes\":{}},\"ai-heading-anhonesthost-1\":{\"type\":{\"resolvedName\":\"Heading\"},\"isCanvas\":false,\"props\":{\"text\":\"AnHonestHost\",\"html\":\"\",\"level\":\"h2\",\"style\":{\"fontSize\":\"36px\",\"fontWeight\":\"700\",\"fontFamily\":\"Inter, sans-serif\",\"color\":\"#1f2937\",\"marginBottom\":\"16px\",\"lineHeight\":\"\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"AnHonestHost Heading\",\"node_id\":\"ai-heading-anhonesthost-1\"},\"displayName\":\"Heading\",\"custom\":{},\"parent\":\"ai-container-anhonesthost-1\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-text-anhonesthost-1\":{\"type\":{\"resolvedName\":\"TextBlock\"},\"isCanvas\":false,\"props\":{\"text\":\"AnHonestHost provides transparent, no-nonsense web hosting for individuals and businesses. Straightforward pricing, honest support, and reliable infrastructure \u2014 no surprises.\",\"html\":\"\",\"style\":{\"fontSize\":\"16px\",\"lineHeight\":\"1.6\",\"color\":\"#3f3f46\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"AnHonestHost Description\",\"node_id\":\"ai-text-anhonesthost-1\"},\"displayName\":\"Text\",\"custom\":{},\"parent\":\"ai-container-anhonesthost-1\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-divider-1\":{\"type\":{\"resolvedName\":\"Divider\"},\"isCanvas\":false,\"props\":{\"color\":\"#e2e8f0\",\"thickness\":\"1px\",\"style\":{},\"aiName\":\"Divider 1\",\"node_id\":\"ai-divider-1\"},\"displayName\":\"Divider\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-columns-streamertools-1\":{\"type\":{\"resolvedName\":\"ColumnLayout\"},\"isCanvas\":false,\"props\":{\"columns\":2,\"split\":\"60-40\",\"gap\":\"40px\",\"style\":{\"paddingTop\":\"60px\",\"paddingBottom\":\"60px\"},\"anchorId\":\"streamertools\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\Line truncated
|
||||
"nav": {
|
||||
"label": "Our Brands"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "page_mtxec41h_2",
|
||||
"name": "About",
|
||||
"slug": "about",
|
||||
"craftState": "{\"ai-hero-about-1\":{\"type\":{\"resolvedName\":\"HeroSimple\"},\"isCanvas\":false,\"props\":{\"heading\":\"About OLS Digital\",\"subtitle\":\"Who we are and what we believe in.\",\"ctas\":[],\"bgType\":\"color\",\"bgColor\":\"#1e293b\",\"bgGradientFrom\":\"#667eea\",\"bgGradientTo\":\"#764ba2\",\"bgGradientAngle\":135,\"bgImage\":\"\",\"bgVideo\":\"\",\"overlayColor\":\"#000000\",\"overlayOpacity\":0,\"textColor\":\"#ffffff\",\"buttonBgColor\":\"#3b82f6\",\"buttonTextColor\":\"#ffffff\",\"minHeight\":\"35vh\",\"verticalAlign\":\"center\",\"textAlign\":\"center\",\"anchorId\":\"about\",\"style\":{},\"aiName\":\"About Hero\",\"node_id\":\"ai-hero-about-1\"},\"displayName\":\"Hero\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-about-1\":{\"type\":{\"resolvedName\":\"Section\"},\"isCanvas\":false,\"props\":{\"style\":{\"paddingTop\":\"60px\",\"paddingBottom\":\"20px\"},\"innerMaxWidth\":\"1200px\",\"topDivider\":\"none\",\"topDividerColor\":\"#ffffff\",\"topDividerHeight\":\"50px\",\"bottomDivider\":\"none\",\"bottomDividerColor\":\"#ffffff\",\"bottomDividerHeight\":\"50px\",\"anchorId\":\"our-story\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"About Content\",\"node_id\":\"ai-section-about-1\"},\"displayName\":\"Section\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{\"section-inner\":\"ai-section-about-1__section-inner\"}},\"ai-heading-about-1\":{\"type\":{\"resolvedName\":\"Heading\"},\"isCanvas\":false,\"props\":{\"text\":\"Our Story\",\"level\":\"h2\",\"style\":{\"fontSize\":\"36px\",\"fontWeight\":\"700\",\"fontFamily\":\"Inter, sans-serif\",\"color\":\"#1f2937\",\"marginBottom\":\"16px\",\"lineHeight\":\"\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Our Story Heading\",\"node_id\":\"ai-heading-about-1\"},\"displayName\":\"Heading\",\"custom\":{},\"parent\":\"ai-section-about-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-text-about-1\":{\"type\":{\"resolvedName\":\"TextBlock\"},\"isCanvas\":false,\"props\":{\"text\":\"OLS Digital was founded to bring structure, resources, and long-term support to promising digital brands. Rather than absorbing the businesses we work with, we help them keep their independent identity while benefiting from shared operational support.\",\"style\":{\"fontSize\":\"16px\",\"lineHeight\":\"1.6\",\"color\":\"#3f3f46\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Our Story Text\",\"node_id\":\"ai-text-about-1\"},\"displayName\":\"Text\",\"custom\":{},\"parent\":\"ai-section-about-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-about-1__section-inner\":{\"type\":{\"resolvedName\":\"Container\"},\"isCanvas\":true,\"props\":{\"style\":{\"padding\":\"20px\",\"minHeight\":\"100px\",\"justifyContent\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"tag\":\"div\",\"fullWidth\":false,\"contentWidth\":\"full\",\"anchorId\":\"\",\"cssId\":\"\",\"cssClass\":\"\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false},\"displayName\":\"Container\",\"custom\":{},\"parent\":\"ai-section-about-1\",\"hidden\":false,\"nodes\":[\"ai-heading-about-1\",\"ai-text-about-1\"],\"linkedNodes\":{}},\"ai-section-mission-1\":{\"type\":{\"resolvedName\":\"Section\"},\"isCanvas\":false,\"props\":{\"style\":{\"paddingTop\":\"20px\",\"paddingBottom\":\"60px\"},\"innerMaxWidth\":\"1200px\",\"topDivider\":\"none\",\"topDividerColor\":\"#ffffff\",\"topDividerHeight\":\"50px\",\"bottomDivider\":\"none\",\"bottomDividerColor\":\"#ffffff\",\"bottomDividerHeight\":\"50px\",\"anchorId\":\"our-mission\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Our Mission\",\"node_id\":\"ai-section-mission-1\"},\"displayName\":\"Section\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":falLine truncated
|
||||
},
|
||||
{
|
||||
"id": "page_mtxec41h_3",
|
||||
"name": "Contact",
|
||||
"slug": "contact",
|
||||
"craftState": "{\"ai-hero-contact-1\":{\"type\":{\"resolvedName\":\"HeroSimple\"},\"isCanvas\":false,\"props\":{\"heading\":\"Contact Us\",\"subtitle\":\"Have a question about OLS Digital or one of our brands? We're happy to help.\",\"ctas\":[],\"bgType\":\"gradient\",\"bgColor\":\"#1e293b\",\"bgGradientFrom\":\"#0f172a\",\"bgGradientTo\":\"#334155\",\"bgGradientAngle\":135,\"bgImage\":\"\",\"bgVideo\":\"\",\"overlayColor\":\"#000000\",\"overlayOpacity\":0,\"textColor\":\"#ffffff\",\"buttonBgColor\":\"#3b82f6\",\"buttonTextColor\":\"#ffffff\",\"minHeight\":\"40vh\",\"verticalAlign\":\"center\",\"textAlign\":\"center\",\"anchorId\":\"contact\",\"style\":{},\"aiName\":\"Contact Hero\",\"node_id\":\"ai-hero-contact-1\"},\"displayName\":\"Hero\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-contact-1\":{\"type\":{\"resolvedName\":\"Section\"},\"isCanvas\":false,\"props\":{\"style\":{\"paddingTop\":\"60px\",\"paddingBottom\":\"60px\",\"textAlign\":\"center\"},\"innerMaxWidth\":\"1200px\",\"topDivider\":\"none\",\"topDividerColor\":\"#ffffff\",\"topDividerHeight\":\"50px\",\"bottomDivider\":\"none\",\"bottomDividerColor\":\"#ffffff\",\"bottomDividerHeight\":\"50px\",\"anchorId\":\"contact-info\",\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Contact Info\",\"node_id\":\"ai-section-contact-1\"},\"displayName\":\"Section\",\"custom\":{},\"parent\":\"ROOT\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{\"section-inner\":\"ai-section-contact-1__section-inner\"}},\"ai-heading-contact-1\":{\"type\":{\"resolvedName\":\"Heading\"},\"isCanvas\":false,\"props\":{\"text\":\"Get In Touch\",\"level\":\"h2\",\"style\":{\"fontSize\":\"36px\",\"fontWeight\":\"700\",\"fontFamily\":\"Inter, sans-serif\",\"color\":\"#1f2937\",\"marginBottom\":\"16px\",\"lineHeight\":\"\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Contact Heading\",\"node_id\":\"ai-heading-contact-1\"},\"displayName\":\"Heading\",\"custom\":{},\"parent\":\"ai-section-contact-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-text-contact-1\":{\"type\":{\"resolvedName\":\"TextBlock\"},\"isCanvas\":false,\"props\":{\"text\":\"For all inquiries regarding OLS Digital, AnHonestHost, StreamerTools, or any of our other brands, please reach out via email and our team will respond as soon as possible.\",\"style\":{\"fontSize\":\"16px\",\"lineHeight\":\"1.6\",\"color\":\"#3f3f46\",\"letterSpacing\":\"\",\"textTransform\":\"\",\"fontStyle\":\"\",\"textDecoration\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paddingTop\":\"\",\"paddingRight\":\"\",\"paddingBottom\":\"\",\"paddingLeft\":\"\",\"border\":\"none\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Contact Text\",\"node_id\":\"ai-text-contact-1\"},\"displayName\":\"Text\",\"custom\":{},\"parent\":\"ai-section-contact-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-spacer-contact-1\":{\"type\":{\"resolvedName\":\"Spacer\"},\"isCanvas\":false,\"props\":{\"height\":\"20px\",\"style\":{},\"aiName\":\"Spacer\",\"node_id\":\"ai-spacer-contact-1\"},\"displayName\":\"Spacer\",\"custom\":{},\"parent\":\"ai-section-contact-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-button-contact-1\":{\"type\":{\"resolvedName\":\"ButtonLink\"},\"isCanvas\":false,\"props\":{\"text\":\"info@olsdigital.com\",\"href\":\"mailto:info@olsdigital.com\",\"target\":\"_self\",\"hoverBg\":\"\",\"hoverColor\":\"\",\"style\":{\"backgroundColor\":\"#3b82f6\",\"color\":\"#ffffff\",\"padding\":\"12px 24px\",\"borderRadius\":\"8px\",\"fontWeight\":\"600\",\"fontSize\":\"16px\",\"border\":\"none\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"boxShadow\":\"none\",\"opacity\":\"1\"},\"animation\":\"\",\"animationDelay\":\"0\",\"hideOnDesktop\":false,\"hideOnTablet\":false,\"hideOnMobile\":false,\"aiName\":\"Email Button\",\"node_id\":\"ai-button-contact-1\"},\"displayName\":\"Button\",\"custom\":{},\"parent\":\"ai-section-contact-1__section-inner\",\"hidden\":false,\"nodes\":[],\"linkedNodes\":{}},\"ai-section-contact-1__section-inner\":{\"type\":{\"resolvedName\":\"Container\"},\"isCanvas\":true,\"props\":{\"style\":{\"padding\":\"20px\",\"minHeight\":\"100px\",\"justifyContent\":\"\",\"marginTop\":\"\",\"marginRight\":\"\",\"marginBottom\":\"\",\"marginLeft\":\"\",\"paLine truncated
|
||||
}
|
||||
],
|
||||
"head_code": "",
|
||||
"design": {
|
||||
"primaryColor": "#3b82f6",
|
||||
"secondaryColor": "#8b5cf6",
|
||||
"accentColor": "#10b981",
|
||||
"headingFont": "Inter, sans-serif",
|
||||
"bodyFont": "Inter, sans-serif",
|
||||
"linkColor": "#3b82f6",
|
||||
"successColor": "#10b981",
|
||||
"warningColor": "#f59e0b",
|
||||
"errorColor": "#ef4444",
|
||||
"backgroundColor": "#ffffff",
|
||||
"textColor": "#1f2937",
|
||||
"mutedTextColor": "#6b7280",
|
||||
"borderColor": "#e5e7eb",
|
||||
"borderRadius": "8px",
|
||||
"buttonFont": "Inter, sans-serif",
|
||||
"buttonRadius": "8px",
|
||||
"navStyle": "light",
|
||||
"headCode": "",
|
||||
"favicon": ""
|
||||
},
|
||||
"extras": [],
|
||||
"_rev": "0af66cc775393317",
|
||||
"_base_rev": "3d18b0567d28ea29",
|
||||
"_client_id": "e4633813-f672-4fbb-9618-d16fb97ff90f"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "ols",
|
||||
"project": "ols.project.json",
|
||||
"published": {"index": "ols.index.html", "about": "ols.about.html", "contact": "ols.contact.html"},
|
||||
"_note": "brands is deliberately not listed: its ColumnLayout row renders differently in the canvas than the export (padding longhand / wrapping) -- a component bug unrelated to the base reset"
|
||||
}
|
||||
Reference in new issue
Block a user