1. System Architecture
The Tadbeer TT Content Portal is built on a deliberate, performance-first foundation using pure HTML5, CSS3, and JavaScript ES6+ — with zero reliance on frontend frameworks. This architectural decision was intentional: by eliminating framework overhead, the portal loads instantly on any device and remains fully auditable without build tooling. Every file is a plain HTML document that a browser can open directly, making it trivially simple to inspect, debug, or modify individual assets without understanding a compilation pipeline.
State management and cross-client data synchronization are handled by Supabase, an enterprise-grade backend platform built on PostgreSQL with a real-time WebSocket layer. When a reviewer approves an asset or submits feedback, that state change is written to Supabase and instantly broadcast to all other connected clients via Supabase Realtime channels — no polling, no page refresh required. This makes simultaneous multi-reviewer sessions fully coherent in real time.
The entire portal is deployed globally on Vercel's Edge Content Delivery Network. Assets are served from physical server nodes geographically closest to each visitor, reducing latency to milliseconds regardless of whether the reviewer is in Muscat, Dubai, London, or Singapore. The CI/CD pipeline is fully automated: every git push to the main branch on GitHub triggers a Vercel build job that compiles, tests, and deploys the updated portal to the edge network in approximately 30 seconds — no manual server operations required at any step.
Beyond the browser, the architecture extends into an automated publishing pipeline. A suite of Node.js scripts residing in the scripts/ directory communicates directly with the Meta Graph API, handling the rendering, image splitting, and programmatic publication of approved content assets to Instagram and LinkedIn. These scripts transform the Tadbeer portal from a review tool into a fully automated, end-to-end content engine.
2. Brand & Design System
The visual language of the content assets served through the portal is a carefully codified design system engineered to project premium B2B authority. The foundational canvas color is Warm Sand Ivory (#F8F6F0), a tone that evokes luxury print materials while providing ideal contrast for both light-background text and overlaid design elements. All structural navigation, headings, borders, and icon elements are rendered in Dark Teal (#184F5B), a color that communicates institutional trust and professionalism — the right impression for a consulting brand operating in the Omani enterprise market. Luxury Gold (#CAA94C) serves as the accent layer, used for border highlights, active state indicators, decorative dividers, and the most important call-to-action elements on each slide, drawing the eye purposefully without overwhelming the composition.
Typography is handled exclusively by the Inter font family, loaded via Google Fonts in four distinct weights: 300 (light, used for supporting body text), 400 (regular, used for standard prose), 600 (semibold, used for slide headings and subheadings), and 700 (bold, used for primary slide titles and statistics). This weight range provides sufficient typographic hierarchy to create visually rich slides without introducing additional typefaces that would complicate brand consistency.
All content assets are built on a fixed-canvas architecture set to exactly 1080 pixels in width — the native resolution expected by Instagram and LinkedIn for optimal quality on high-DPI displays. The challenge this creates is that a 1080px-wide fixed layout would overflow horizontally on any standard mobile screen. To solve this elegantly without compromising the layout, the design system implements a proprietary CSS Mobile Auto-Fit technique. Using a media query that activates at screen widths below 1100px, the following zoom expression is applied to every content container class: zoom: calc((100vw - 20px) / 1080). This calculation dynamically computes the ratio between the available viewport width and the fixed 1080px canvas, applying it as a uniform zoom scale. The result is that every slide — whether a .slide, .story-slide, .post-card, .square-slide, or .story-card — scales proportionally to fill the phone screen perfectly, preserving every typographic proportion and spacing relationship exactly as designed, with no horizontal scrollbar.
3. 26 Content Assets
The portal manages a strategically structured 15-day content pipeline comprising 26 distinct assets deployed across three primary format categories, each engineered for the specific technical and behavioral requirements of Instagram and LinkedIn's content algorithms and audience expectations.
8 Carousels — carousels/ directory
Carousels are multi-slide HTML decks rendered at 1080x1350px (4:5 portrait ratio), optimized for both Instagram and LinkedIn feed placement where the portrait format maximizes screen real estate. Each carousel functions as a self-contained educational narrative — a micro-course presented in swipeable slides. The Instagram set includes: ig_day02 (7 Signs Your Business Is Ready to Scale — a diagnostic checklist for SME owners), ig_day04 (Marketing vs System — reframing the entrepreneur's growth strategy), ig_day09 (1200 OMR Ad Spend ROI — a before/after case study demonstrating system ROI), and ig_day11 (Employee Quits Without SOP — illustrating operational dependency risk). The LinkedIn set, calibrated for a more analytical professional audience, includes: li_day02 (What Tadbeer Does — a clear brand positioning deck), li_day04 (Founder ERP Guide — demystifying enterprise resource planning for founders), li_day09 (Customer System — building scalable client acquisition), and li_day13 (Omanization — workforce localization strategy and compliance).
5 Story Sequences — story_sequences/ directory
Story sequences utilize the 1080x1920px (9:16 vertical format), the native aspect ratio of Instagram and LinkedIn Stories. Each sequence is structured as a three-slide interactive journey specifically designed to maximize viewer completion rates and active engagement through poll sticker mechanics. Poll stickers invite the audience to tap a response, signaling intent and generating algorithm-boosting engagement signals. The five sequences cover: Day01 (weekend poll — calibrating audience pain points), Day07 (Friday engagement — leveraging end-of-week reflection behavior), Day08 (weekend research — capturing audiences in discovery mode), Day14 (back to school — business season framing), and Day15 (Dhofar — regional relevance and cultural resonance with the Omani market).
13 Posts & Reel Covers — posts/ directory
Single-image visual cards at either 1080x1350px or 1080x1080px, these are the highest-frequency content format in the pipeline. They function as standalone post images or as the cover frame for short-form video reels. The Instagram set covers high-attention hooks and bold business truths: ig_day03 (followers), ig_day05 (AI 15-person team), ig_day06 (turned down 15k client), ig_day10 (WhatsApp is not CRM), ig_day12 (stop posting more), and ig_day13 (marble leakage). The LinkedIn set adopts a more professional, insight-driven tone: li_day02 (5 problems), li_day04 (marble case study), li_day06 company (Khareef team), li_day06 founder (process whiteboard), li_day09 (ad spend stat), li_day11 (hidden cost), and li_day13 (cultural games).
4. Public Reviewer Portal (index.html) — Step-by-Step Guide
The public reviewer portal at index.html is the primary stakeholder-facing interface for evaluating the 15-day content pipeline. It is intentionally accessible without any login — any team member with the URL can open it immediately. The following steps describe the complete review workflow from first load to final approval.
Open the portal URL in any modern browser. The page loads a card grid layout — a responsive CSS Grid displaying all 26 content assets as individual interactive cards. Each card prominently displays the asset name, its content category (Carousel, Story, or Post), and a color-coded status badge: grey for Pending, green for Approved, and amber for Changes Needed. This grid view provides an immediate, at-a-glance overview of the entire pipeline's review progress.
To inspect any asset, click the View Post button on its card. This opens the raw HTML file for that specific asset in a new browser tab at its native 1080px width. The reviewer sees the exact design, typography, color palette, and copy as it will appear when rendered to a PNG image for publication. On a desktop browser, this is a full-resolution preview. On mobile, the CSS Mobile Auto-Fit zoom technique (described in Section 2) scales it to fit perfectly within the phone screen.
After reviewing the asset, the reviewer enters their name in the Reviewer Name field. This field is mandatory — all feedback and approvals are attributed to a named individual, creating a transparent accountability record in the database.
If the asset is approved, the reviewer clicks the Approve button. Immediately, two things happen simultaneously: the card's status badge animates and changes to green ("Approved"), providing instant visual confirmation in the UI, and a network request is dispatched to Supabase writing the approval — status: 'approved', the reviewer name, and a timestamp — to the content_feedback table. Because of Supabase Realtime, every other reviewer currently on the portal sees this card flip to green on their screen within milliseconds, without any page refresh.
If the asset requires changes, the reviewer types their feedback into the text area beneath the card. This field implements a 600ms debounce — a performance optimization technique that instructs the JavaScript code to wait 600 milliseconds after the reviewer's last keystroke before firing the network write to Supabase. This prevents rapid-fire database writes on every single character typed. Without debouncing, typing a 100-character feedback note would trigger 100 separate API calls. With the 600ms debounce, it triggers only one — fired 600ms after the final character, when the reviewer has paused, indicating they have likely finished typing. The status is simultaneously set to 'changes_required'.
If Supabase becomes temporarily unavailable — due to network interruption or service maintenance — the portal does not lose the reviewer's work. Instead, it silently falls back to writing feedback data to the browser's localStorage, a client-side key-value store built into every browser. A small indicator notifies the reviewer that they are in offline mode. When connectivity is restored, the portal detects the restoration, reads the queued data from localStorage, and syncs it to Supabase — ensuring no feedback is ever lost to a temporary outage.
5. Admin Command Center (admin.html) — Step-by-Step Guide
The Admin Command Center at admin.html is the operational dashboard for the content management team. It is the real-time nerve center for tracking review progress, actioning feedback, and triggering the publishing pipeline.
Open admin.html. At the top of the page, the live counter dashboard displays three key statistics: total assets (26), approved count, and pending/changes count. Beneath the counters, animated progress bars fill from left to right as the approved count increases, providing an intuitive, gestalt sense of pipeline completion status without requiring the administrator to read specific numbers.
Use the filter tabs — All, Approved, Changes Needed, Pending — to isolate assets by their review state. Clicking "Changes Needed" instantly filters the table to show only assets requiring revision, allowing the administrator to work through them sequentially without manually scanning the full list.
Use the search bar to filter by asset name in real time. Typing "day09" instantly narrows the table to all assets from Day 9, regardless of platform or format. This is useful when addressing feedback for a specific day's content cluster.
The main data table displays every asset with five columns: Asset Name, Reviewer Name, Feedback Text (the full comment from the reviewer), Status badge, and Last Updated timestamp. All columns are readable at a glance and sortable by clicking column headers.
Click the Copy Feedback Report button to generate a comprehensive report. The system iterates through all 26 assets in the database and builds a plain-text block in the following format for each asset: the asset name, its current status, the reviewer's name, and their full feedback comment. The complete report — covering all 26 entries — is automatically copied to the system clipboard. The administrator can then open WhatsApp, paste directly into a group chat with the design team, and send. No formatting required, no screenshots needed.
The dashboard auto-refreshes every 30 seconds in the background, pulling the latest state from Supabase. This ensures the administrator always has an up-to-date view of what reviewers are approving or flagging in real time, even if they step away from their desk and return.
To action a "Changes Needed" feedback note: open the corresponding HTML asset file in your code editor (e.g., VS Code), implement the specific revision described in the feedback, then run git add ., git commit -m "fix: [asset name] — [description of change]", and git push. Vercel detects the push event via a GitHub webhook, executes a fresh build, and propagates the updated file to the global edge CDN in approximately 30 seconds. The reviewer can then open the View Post link again to verify the change and flip the status to Approved.
6. Supabase Schema
The data persistence layer for the review platform is powered by a precisely structured PostgreSQL table hosted on Supabase. The schema is minimal by design — it captures exactly the data required for review state management without unnecessary complexity. The table is named content_feedback.
The id column is a TEXT PRIMARY KEY that matches the asset's filename without its extension (e.g., ig_day02 for the file carousels/ig_day02.html). This design choice creates an implicit, zero-overhead join between the database record and the static file — no foreign key table or lookup required. The status field accepts exactly three values: 'pending' (initial default state), 'approved' (reviewer has signed off), and 'changes_required' (revisions needed). The feedback TEXT field stores the reviewer's written notes. The reviewer_name TEXT field attributes every action. The updated_at TIMESTAMPTZ field is automatically maintained by a PostgreSQL trigger that fires on every row update, ensuring the Admin Command Center always shows precisely when each asset was last acted upon.
Row Level Security is configured to allow full anonymous CRUD access — any client with the Supabase URL and anon key can read and write the table. This removes the friction of authentication for external stakeholders during the fast-paced review cycle. Supabase Realtime is activated using the following SQL, which adds the table to the logical replication publication used by the WebSocket broadcast layer:
-- Create the review state table
CREATE TABLE content_feedback (
id TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending', 'approved', 'changes_required')),
feedback TEXT,
reviewer_name TEXT,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Auto-update updated_at on every row change
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER set_updated_at
BEFORE UPDATE ON content_feedback
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- Enable Supabase Realtime broadcast for this table
ALTER PUBLICATION supabase_realtime ADD TABLE content_feedback;
-- Allow anonymous read and write (RLS)
ALTER TABLE content_feedback ENABLE ROW LEVEL SECURITY;
CREATE POLICY "anon_all" ON content_feedback
FOR ALL TO anon USING (true) WITH CHECK (true);
7. Scripts Directory
The scripts/ directory contains the Node.js automation infrastructure that bridges the review platform to the live social media publishing channels. These scripts operate independently of the browser-based portal and are executed from the command line or triggered by the cron daemon.
instagram_publisher.js
The core publication module that communicates directly with the Meta Graph API. It accepts a list of image file paths and a post configuration object (caption, post type, scheduled time). For single-image posts, it uploads the binary to Meta's media server and calls the publish endpoint. For carousels, it uploads each slide image individually, collects their media container IDs, creates a carousel container referencing all of them, and then executes a single publish call against the container. For video reels, it handles the asynchronous upload-and-process cycle Meta requires before a video can be published. The script includes exponential backoff retry logic to handle transient API rate-limit errors gracefully.
get_long_lived_token.js
A maintenance utility that manages Meta's token lifecycle. The Meta Graph API issues short-lived user access tokens with a 1-hour expiry, which are unusable for an automated publishing daemon. This script exchanges a freshly generated short-lived token for a 60-day long-lived token by calling the /oauth/access_token endpoint with the appropriate grant parameters. The resulting long-lived token is written to the local .env file, making it available to instagram_publisher.js and cron_daemon.js without requiring manual re-authentication for two months.
cron_daemon.js
A persistent scheduled process that monitors a configured publishing calendar. The calendar defines the optimal posting time for each of the 26 assets — typically targeting peak engagement windows in the Gulf timezone (Sunday through Thursday, 8–9 AM and 7–9 PM GST). When the daemon's internal clock matches a scheduled event, it triggers the corresponding rendering and publishing sequence: invoking render_and_split.js to generate the image files, then passing the results to instagram_publisher.js for upload. The daemon is designed to run continuously as a background process and writes a structured log file for every publishing event, capturing success confirmations, API response codes, and any error states.
render_and_split.js
The most technically complex script in the suite, powered by Puppeteer — Google's headless Chromium browser API for Node.js. When invoked with an HTML asset file path, Puppeteer boots a headless browser instance, navigates to the file using a file:// URI, waits for all fonts and images to fully load, and captures a full-resolution screenshot of the rendered page. For single-image assets, this produces one PNG file. For multi-slide carousels, the script iterates through each slide element, captures each one as a separate screenshot, and saves them as sequentially numbered files (e.g., ig_day02_slide_1.png, ig_day02_slide_2.png). Each output PNG is exactly 1080px wide and sized to the correct height for its format, ready for upload to Meta's servers without any additional image processing.
8. Step-by-Step Publishing Workflow
The complete lifecycle of a content asset — from first review to final publication on Instagram and LinkedIn — follows a rigorous eight-step procedural workflow that integrates all three layers of the system: the reviewer portal, the admin command center, and the automated publishing scripts.
The designated reviewer opens the public portal at the live URL. They scan the card grid and identify assets currently showing a Pending status. They click View Post on the first pending asset to open a full-resolution preview of the HTML rendering in a new browser tab, inspecting every design element, typographic choice, and copy line.
The reviewer enters their name in the Reviewer Name field, then either clicks Approve (the card flips green and the Supabase record is updated instantly) or types detailed revision notes into the feedback text area (the 600ms debounce fires, writing the feedback and setting status to changes_required).
The content administrator opens the Admin Command Center (admin.html). They click the "Changes Needed" filter tab to isolate flagged assets. They read each reviewer comment in the Feedback column of the table, understanding exactly what revision is required for each asset.
For each flagged asset, the administrator opens the corresponding HTML file in their local code editor. They implement the specific revision — adjusting copy, updating a color, resizing a graphic element, or correcting a statistic — making targeted, surgical edits to the HTML and CSS.
The administrator runs git add ., writes a descriptive commit message (e.g., fix: ig_day09 — updated ROI figure to 3.2x per reviewer feedback), and executes git push. Within 30 seconds, Vercel's CI/CD pipeline detects the push, rebuilds and deploys the updated file, and the reviewer's View Post link serves the corrected version. The reviewer re-inspects and flips the status to Approved.
Once an asset achieves Approved status, cron_daemon.js — running as a background process on the deployment server — identifies it during the next scheduled publishing cycle. The daemon checks the current time against the publishing calendar and, when the configured peak-engagement window arrives, initiates the automated rendering sequence.
The daemon invokes render_and_split.js, which boots a headless Chromium instance via Puppeteer, navigates to the approved HTML asset, waits for all assets to fully render, and captures pixel-perfect PNG screenshots at 1080px width. Multi-slide carousels are split into sequentially numbered individual image files. All PNGs are written to a temporary output directory.
Finally, instagram_publisher.js takes control. Authenticating via the stored 60-day long-lived token, it uploads each PNG file to Meta's media servers, assembles carousel containers where applicable, and executes the publish call — pushing the content live to the Tadbeer TT Instagram and LinkedIn accounts precisely at the scheduled peak-engagement time. The daemon logs the full API response and marks the asset as published in its internal state registry.