# Fourge Portal Layout System This is the single source of truth for dashboard/profile visual structure and UI geometry. ## 1) Global Frame - Viewport app shell: `height: 100vh`, `overflow: hidden` - Main content gutter: `24px` all sides - Sidebar: `width: 76px`, `top: 24px`, `left: 24px`, `height: calc(100vh - 48px)`, `border-radius: 8px` - Main wrapper offset from sidebar: `margin-left: 100px` - Page rhythm unit: `24px` (header spacing, card gaps, section gaps) ## 2) Theme + Background - Background ownership is `body` via `background: var(--bg)`. - Dark base token `--bg` is full gradient: - radial glow + vertical dark gradient. - Light base token `--bg` is full gradient: - gray radial glow + white vertical gradient. - Do not use `html` theme-gradient scripting for Safari chrome behavior. ## 3) Tokens - Accent: `#F5A523` - Card bg dark: `rgba(255,255,255,0.02)` - Card bg light: `rgba(0,0,0,0.02)` - Sidebar/menu shell uses the same transparent card background token as cards - Secondary card tone dark: `rgba(255,255,255,0.08)` - Secondary card tone light: `rgba(0,0,0,0.08)` - Border dark: `rgba(245,165,35,0.15)` - Border light: `rgba(0,0,0,0.1)` - Text primary dark/light: `#ffffff / #0d0d0d` - Text secondary dark/light: `#a8a8a8 / rgba(0,0,0,0.6)` - Text muted dark/light: `#666666 / rgba(0,0,0,0.38)` ## 4) Typography - Font family: `Fourge`, then `-apple-system`, `BlinkMacSystemFont`, `'Segoe UI'`, `sans-serif` - Base font size: `14px` - Header title: `28px`, `500`, `line-height: 1.2` - Header subtitle: `13px` - Widget title: `11px`, `500`, uppercase, `letter-spacing: 0.8px` - Section header (new standard): `18px`, `500`, Title Case, `letter-spacing: 0.2px`, `line-height: 1.1` - Body table text: `12px/13px` by column importance ## 5) Card System - Default widget shell: - `background: var(--card-bg)` - `border: 1px solid var(--border)` - `border-radius: 8px` - `padding: 18px 21px` - `backdrop-filter: blur(12px)` + `-webkit-backdrop-filter` - Card width is not an intrinsic card token. - Width is owned by the page/grid/container the card lives in. - Cards should fill the slot provided by that page layout unless a page-level rule explicitly fixes a track. - Compact card radius (legacy generic `.card`): `4px` (do not use for new dashboard widgets) ## 6) Header + Top Right Controls - Site header: `padding-top: 24px`, `padding-bottom: 24px` - Right control row: - Search icon button: `32x32` - Search button to theme toggle space: `7px` (`search-wrap margin-right`) - Theme toggle: `32x32` - Theme toggle to avatar: `14px` (`avatar-wrap margin-left`) - Avatar button: `49x49`, circle, `2px` inner ring + `2px` accent outline ## 6.5) Section Control Bars (Tabs + Actions) - For page-level card controls (ex: Tasks/Projects, Finances tabs): - container uses: `display: flex`, `align-items: center`, `gap: 4`, `margin-bottom: 10`, `flex-shrink: 0` - tabs stay on the left in source order - action buttons group sits on the right using: `margin-left: auto`, `display: flex`, `align-items: center`, `gap: 8` - do not use hardcoded spacer blocks (`width` filler divs) to force alignment - icon-only filter/action buttons share the same row and align vertically with add buttons ## 7) Dashboard Grids (Team) - Stat row: `grid-template-columns: 1fr 1fr 1fr 1.5fr`, `gap: 24`, `margin-bottom: 0` - Row 2: `grid-template-columns: 1fr 1fr 280px`, `gap: 24`, `margin-top: 24` - Order: Recent Activity (left), Tasks In Progress (center), Calendar (right, fixed width) - Row 3: `grid-template-columns: 1fr 1fr`, `gap: 24`, `margin-top: 24` - Row 4 full-width: `margin-top: 24` ## 8) Stat Cards - Card min height: `120px` - Internal row gap: `21px` - Label/value/sub spacing: - Label: `margin-bottom: 5px` - Value: `30px`, `400`, `letter-spacing: -0.5`, `line-height: 1.1` - Sub: `12px`, `margin-top: 5px` - Icon badge: `27x27`, circle - Icon glyph: `13x13` ## 9) Calendar - Card uses widget shell - Header-to-grid gap: `14px` - Weekday label: `10px`, `600`, `letter-spacing: 0.5` - Day cell button: `28x28`, circular - Day number: `12px` - Today style: bg `#F5A523`, text `#0d0d0d`, `700` - Dots: up to 3, each `3x3`, gap `2` - Popover: - Anchored left of cell: `right: calc(100% + 8px)`, vertical centered - `width: 210px`, `padding: 10px 12px`, `border-radius: 8px` - shadow `0 12px 32px rgba(0,0,0,0.45)` - row dot `6x6`, row text `12px` ## 10) Activity + Performance Rows - Visible rows target: 5 - Row layout: `display:flex`, `align-items:center`, `gap:10px` - Row spacing: `margin-top: 10px` from second row onward - Name text: `13px` - Meta/date text: `11px` - Progress track: `height: 4px`, `radius: 2px` - Percentage width slot: `min-width: 28px` - Empty states in all cards (dashboard, profile, tasks, projects, file sharing, etc.): - any `No ...` message is centered in the card body (`display:flex`, `align-items:center`, `justify-content:center`) - use shared class treatment (`.card-empty-center`) for consistency - avoid top-offset-only placement for empty text ## 11) Tables - General table layout in dashboard cards: `table-layout: fixed`, `border-collapse: collapse` - Column widths are table-specific, not universal tokens. - Widths belong to the individual table/view that defines them. - Reuse widths only when the same table pattern is intentionally repeated. - Header cells: - `font-size: 10px`, `font-weight: 500`, uppercase, `letter-spacing: 0.6px` - bottom spacing: `padding-bottom: 12px` - sticky behavior for scrollable tables: - table headers stay fixed while body scrolls - use shared sticky-head treatment for all app tables (`position: sticky; top: 0`) - table scrollbars are visually hidden for table scroll containers; wheel/trackpad scrolling remains active - Body cells: - primary text: `13px` - secondary/metrics text: `12px` - row vertical spacing via cell padding: typically `5px` - Hot Tasks column widths: - check `10%`, task `40%`, requested by `35%`, due by `15%` - Client Highlight column widths: - icon `5%`, company `22%`, contact `23%`, projects `13%`, open `13%`, outstanding `12%`, paid `12%` - Sorting rule: - Every visible data column header must be sortable. - Use clickable header controls (`SortTh`) with ascending/descending indicator. - Exclude only non-data utility/action columns (checkbox-only, icon-only status marker, action buttons). ## 12) Profile Page - Container: full available content width, column, `gap: 24` - Top row: `grid-template-columns: 60fr 40fr`, `gap: 24` - At `<=1200px`: top row stacks to one column - Main profile card uses widget shell - Profile card width is determined by the profile page grid, not by the card itself. - Internal card layout: - row `gap: 20px` - portrait max `140x140`, circle - portrait aligns flush in the row without extra wrapper padding/side space - detail grid `140px 1fr`, `row-gap: 8`, `column-gap: 12`, `margin-top: 14` - profile name/title: `18px`, `500`, `line-height: 1.2` - company subtitle: `13px`, secondary text - contact/detail rows: `13px`, primary text - social row `margin-top: 14`, `gap: 8` - company line under title is always visible; fallback display is `—` when no company is assigned - right-side meta labels (`Member Since`, `Role`) use widget-title sizing: `11px`, `500`, uppercase, `letter-spacing: 0.8px` - right-side meta values use body sizing: `13px` - self-only edit button: `position: absolute`, `top: 18px`, `right: 21px` (aligns to card padding), `border-radius: 8px` (matches card), `height: 22px`, `font-size: 12px`, `padding: 0 12px` - default outline button hover/focus fills with accent gold and uses dark text - Right calendar card shows only tasks/events assigned to the viewed profile user - Profile page left column includes a `Tasks In Progress` card above `Recent Activity` - Profile `Tasks In Progress` follows the dashboard table/card pattern: - same widget shell and header treatment - same show-all behavior (5 visible by default) - profile-specific filter is tasks assigned to the viewed user with statuses `in_progress`, `on_hold`, or `client_review` - columns are `Task` and `Status` - Profile activity feed action text pattern: `Task started`, `Task submitted`, `Task approved`, `Task rejected` (sentence title case) - Activity feed includes: - actions performed by the viewed user - `Task approved` and `Task rejected` entries for tasks assigned to that viewed user (even if actioned by someone else) ## 12.5) File Sharing - Main split: `grid-template-columns: minmax(0, 1fr) minmax(0, 3fr)` for an effective `25 / 75` layout, `gap: 24` - Left stacked cards (`Pinned`, `Navigation`): `gap: 24` - Canonical folder structure: - team and subcontractor profile folders live at `Team/` - company folders live at `Clients/` - project folders live at `Clients//Projects/` - every project auto-creates `00 Project Files` - task folders live at `Clients//Projects//` - every task auto-creates `Request Info`, `Working Files`, and `Old Books` - request submission files are copied into `Request Info/R00`, `R01`, and higher revision folders - renaming a team user, subcontractor user, company, project, or task must rename the matching filesystem folder - folder repair/backfill is additive only: create missing folders, leave existing folders untouched - request-info backfill must skip files that already exist and must not overwrite added working files or user-managed files - deleting a task in the portal moves its task folder into `Archive/Clients//Projects//` instead of deleting it - deleting a project in the portal moves its project folder into `Archive/Clients//Projects/` instead of deleting it - orphan reconciliation may move folders into archive when they no longer map to portal records - orphan team folders move to `Archive/Team/` when no team or subcontractor profile matches that folder name - orphan project folders move to `Archive/Clients//Projects/` when no portal project matches that folder name under that company - orphan reconciliation does not remove company roots or overwrite archived content; it merge-moves into archive - Access/home rules: - team members have full file access - client users with exactly one tied company use that company folder as home: `/Clients/` - client users tied to multiple companies use `/Clients` as home and only see company folders they are tied to - `/Clients` acts as a virtual parent for multi-company client users and must not expose unrelated company folders - subcontractor users use virtual home `/` - subcontractor virtual home shows `Team` plus only the project folders they are assigned to - subcontractor users skip all other root folders and must not see unrelated client or internal roots - subcontractor `Team` maps to the real shared `Team` folder - each subcontractor project folder is a virtual root that maps to the real client project path `/Clients//Projects/` - Right card header: - title label is `File Browser` - drag/drop note sits directly under the title and uses subtext sizing: `12px` - current file path uses the same `11px` uppercase label treatment as the title row family - toolbar does not show `+ Folder` or `Upload` buttons - upload happens by dragging and dropping files or folders into the right card - delete action uses the same geometry as the profile edit button (`top: 18px`, `right: 21px`, `8px` radius, `22px` height, `12px` text, `0 12px` padding) but uses danger styling with red hover/fill - Right card table: - uses dashboard table styling (`table-layout: fixed`, transparent header row, `10px` uppercase headers, `13px/12px` body text) - card padding matches dashboard widget shell (`18px 21px`) - clickable text inside the table highlights in accent; rows do not use block hover fill - body cells are borderless like dashboard card tables - body cell padding matches dashboard tables exactly: `5px` - visible headers (`Name`, `Size`, `Modified`) use shared sortable header controls - file/folder icon footprint is compact so row height stays on the same cadence as dashboard tables - alternating rows use a very subtle theme-aware tint for scanability - Navigation tree: - `Home` aligns to the first arrow column - connector line runs through the arrow center column - pinned folders are user-specific and must not be shared across users on the same device/browser session - hover is text-only accent color, no row fill ## 13) Radius + Geometry Rules - Dashboard/profile widgets: `8px` radius - Sidebar: `8px` radius - Standard app buttons use the `Edit Profile` geometry: - geometry is tokenized globally in CSS vars and must be reused (no per-page hardcoded button geometry): - `--btn-height` - `--btn-padding-x` - `--btn-radius` - `--btn-font-size` - `--btn-font-weight` - `--btn-letter-spacing` - `--btn-line-height` - height: `22px` - horizontal padding: `0 12px` - border-radius: `8px` - font-size: `11px` - font-weight: `500` - letter-spacing: `0.8px` - text-transform: uppercase - line-height: `1` - label color follows theme text roles: - dark mode: primary button labels use dark text on accent fill; outline labels use primary text on transparent surface - light mode: outline labels use light-theme primary text; danger labels use danger text - outline button border uses the same `var(--border)` token as card outlines unless a button role explicitly overrides it - keep existing button role styling intact (`outline`, `primary`, `danger`); geometry changes, not role colors - Inputs/dropdowns may use their own control radius unless a page-specific rule says otherwise - Circular elements (avatar/day/icon badges): `50%` ## 14) Z-Index Stack - Sidebar: `200` - Header dropdowns/tooltips: `300` - Calendar hover popover: `1002` within card context (`card can be 1001 active`) - Modal overlay: `1200` ## 14.5) Loading Popup - Use shared `PageLoader` component for loading overlays instead of page-specific popup implementations. - Loading popups/overlays, including shared site-wide page loaders and file-sharing loading states, use the same widget shell as dashboard cards: - `background: var(--popup-bg)` - `border: 1px solid var(--border)` - `border-radius: 8px` - `padding: 18px 21px` - `backdrop-filter: blur(12px)` + `-webkit-backdrop-filter` - Popup surface is theme-aware: - dark mode: deep translucent dark surface - light mode: bright translucent white surface - Loading overlay scrim is theme-aware: - dark mode: `rgba(0,0,0,0.58)` - light mode: soft light scrim via `--overlay-scrim` - Loading title uses widget header treatment: `11px`, `500`, uppercase, `letter-spacing: 0.8px`, secondary text - Progress track: - height: `4px` - radius: `2px` - track color: secondary card tone (`var(--card-bg-2)`) - progress fill: accent - Progress/meta text under the bar uses subtext sizing (`12px`) and secondary text ## 14.6) Modal Form Fields - Modal forms that follow `Edit Profile` styling (including `Add Task`/`New Task`) share the same field typography and theme tokens: - modal surface transparency matches `Edit Profile` card shell (`background: var(--card-bg)`), not a denser popup-specific override - field labels: `11px`, `500`, uppercase, `letter-spacing: 0.8px`, color `var(--text-secondary)` - input/select text: `13px`, color `var(--text-primary)`, left aligned - textarea text: `13px`, color `var(--text-primary)` - field surfaces and borders use global theme tokens (`var(--card-bg-2)` + `var(--border)`) for dark/light parity - modal form action buttons use the standard outline geometry/style (`btn btn-outline`) unless a page explicitly defines a different role ## 14.7) Status Tags - Shared status tags use `StatusBadge` (`.badge.badge-status`). - Base badge geometry: - `display: inline-flex` - `align-items: center` - `gap: 4px` - `padding: 3px 10px` - `border-radius: 4px` - `font-size: 11px` - `font-weight: 400` - `letter-spacing: 0.3px` - `white-space: nowrap` - Status badge geometry override (`.badge-status`): - `min-width: 78px` - `height: 20px` - `justify-content: center` - `padding: 0 8px` - `font-size: 10px` - `line-height: 1` - Status colors are variant classes (`.badge-not_started`, `.badge-in_progress`, etc.) and are theme-aware. - Exception: compact urgent tag (`.badge-needs_revision`) uses tighter geometry: - `min-width: 28px` - `padding: 3px 4px` - `border-radius: 4px` - fixed red treatment in both dark/light themes. ## 15) Motion - Motion vars: - fast `160ms` - base `220ms` - easing `cubic-bezier(0.22, 1, 0.36, 1)` - Dropdown animation: `ui-fade-up` from `translateY(4px)` + opacity 0 -> 1 ## 17) Hover Interaction Contract - Sidebar, header icon buttons, dropdown items, and avatar menu items must show a visible hover surface before click. - Single hover source-of-truth block controls these elements. - Dark hover surface baseline: `#1f1f1f`. - Light hover surface baseline: `rgba(0,0,0,0.08)`. - Nav icon opacity must lift from muted to full on hover (`opacity: 1`). - Clickable text links use text-only highlight on hover/focus (`color: var(--accent)`), not whole-row/background fill. - Hover and active must be visually distinct: - hover uses stronger temporary contrast (`bg` + thin border), - active remains persistent selected-state background. - File Sharing left navigation exception: - `Pinned` + `Navigation` tree rows use text-only hover highlight (`color: var(--accent)`). - Do not apply row background fill for hover in this section. ## 16) Non-Negotiable Implementation Rules - Keep gradient backgrounds on `html`, not `body` - Keep widget shell values (`18px 21px`, `8px`, blur 12, border token) consistent - Maintain global `24px` spacing rhythm for page/frame/grid gaps - Keep team dashboard card order: 1. Open Tasks 2. Active Projects 3. Net Profit 4. Revenue (wide) - Keep row 2 order: Recent Activity (left), Tasks In Progress (center), Calendar (right fixed at `280px`) ## 18) Invoice Rules - New work (`R00`) is billed at the new-service rate. - Revision `R01` is free. - Revisions `R02+` are billed incrementally: each uninvoiced revision entry contributes exactly `1x` revision charge. - Previously invoiced revisions are excluded; later invoices only charge newly uninvoiced revisions (e.g. if `R02` was already invoiced, at `R04` only `R03` + `R04` are billed).