diff --git a/web/static/app.css b/web/static/app.css index 461ba17e..2cb762e8 100644 --- a/web/static/app.css +++ b/web/static/app.css @@ -5,18 +5,18 @@ the @layer line in ui/base.html: above Tailwind's preflight, below Tailwind's utilities. So this file is still the site's look, and a Tailwind class on one element still overrides it. Do not unwrap it — unlayered CSS beats every - layer, which would put this file above the utilities again. */ + layer, which would put this file above the utilities again. + + Every color in this file must be a var(--token); add a new token pair to + :root/:root.dark rather than writing a literal hex. */ @layer app { /* ---------- Theme tokens ---------- - Only the colours reused across several rules are variables here; the - rest of the file is still one-off hex per rule (see the file header for - why). Dark is the default — the inline script in ui/base.html puts - `dark` on before first paint unless localStorage says "light" — - so :root itself carries the dark values and :root.light carries the - original light ones. Values line up with the Penguin UI dark palette in - ui/base.html's @theme block where a token overlaps with one of these. */ + The inline script in ui/base.html puts `dark` on before first paint + unless localStorage says "light", so :root is the light palette and + :root.dark is the dark palette. Penguin UI aliases these same properties + in ui/base.html's @theme block. */ :root { --bg: #f3f5f7; @@ -43,6 +43,34 @@ --input-bg: #ffffff; --input-border: #cbd3dd; --input-text: #1e2938; + --on-primary: #ffffff; + --secondary: #152238; + --on-secondary: #ffffff; + --outline-strong: #8f9bad; + --info: #1d4e89; + --on-info: #ffffff; + --warning: #8a5a08; + --on-warning: #ffffff; + --on-danger: #ffffff; + --on-success: #ffffff; + --success-strong: #067647; + --error: #b42318; + --error-border: #f1c4c0; + --info-bg: #eaf2ff; + --info-border: #cfdcef; + --info-hover-bg: #d8e8ff; + --danger-hover-bg: #fbe6e6; + --success-bg: #f2f9f3; + --success-border: #cfe0c4; + --warning-bg: #fdf3e9; + --warning-border: #f0cfae; + --code: #315078; + --link: #315f9d; + --disabled-bg: #f7f8fa; + --disabled-text: #adb6c3; + --shadow-color: 31 43 58; + --focus-color: 37 99 235; + --toolbar-bg: rgb(255 255 255 / 96%); } /* Dark is the default — see the inline script in ui/base.html, which puts @@ -74,6 +102,34 @@ --input-bg: #101a29; --input-border: #33415a; --input-text: #e5ebf3; + --on-primary: #0f1722; + --secondary: #c7d3e6; + --on-secondary: #152238; + --outline-strong: #cbd7e8; + --info: #8fbfff; + --on-info: #0f1722; + --warning: #e4b45d; + --on-warning: #17202a; + --on-danger: #241113; + --on-success: #10251a; + --success-strong: #8fe0aa; + --error: #f08b82; + --error-border: #6f3a3c; + --info-bg: #1d3553; + --info-border: #3f5f8e; + --info-hover-bg: #2b456b; + --danger-hover-bg: #512b31; + --success-bg: #203a2b; + --success-border: #456848; + --warning-bg: #4a3420; + --warning-border: #765634; + --code: #9abdf0; + --link: #8fbfff; + --disabled-bg: #202c3e; + --disabled-text: #64748b; + --shadow-color: 0 0 0; + --focus-color: 143 191 255; + --toolbar-bg: rgb(21 34 56 / 96%); } /* ---------- Foundations ---------- */ @@ -101,51 +157,51 @@ .heading { display: flex; justify-content: space-between; align-items: end; gap: 24px; margin-bottom: 22px; } .heading h1 { margin: 0; font-size: 28px; } - .heading p { margin: 5px 0 0; color: #687384; } + .heading p { margin: 5px 0 0; color: var(--text-subtle); } .eyebrow { color: var(--primary) !important; font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; } .actions { display: flex; flex-wrap: wrap; gap: 8px; } - .actions a, .actions span { padding: 8px 12px; border: 1px solid #cfd7e1; border-radius: 7px; color: #283548; background: var(--surface); text-decoration: none; } + .actions a, .actions span { padding: 8px 12px; border: 1px solid var(--border); border-radius: 7px; color: var(--text-secondary); background: var(--surface); text-decoration: none; } /* ---------- Admin three-pane browser ---------- */ - .workspace { display: grid; grid-template-columns: minmax(210px, .75fr) minmax(280px, 1.15fr) minmax(300px, 1.25fr); min-height: 560px; border: 1px solid var(--border); border-radius: 11px; overflow: hidden; background: var(--surface); box-shadow: 0 10px 26px rgb(31 43 58 / 7%); } - .pane + .pane { border-left: 1px solid #e1e5eb; } - .pane-title { height: 54px; padding: 0 17px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e5e8ed; background: var(--surface-alt); } + .workspace { display: grid; grid-template-columns: minmax(210px, .75fr) minmax(280px, 1.15fr) minmax(300px, 1.25fr); min-height: 560px; border: 1px solid var(--border); border-radius: 11px; overflow: hidden; background: var(--surface); box-shadow: 0 10px 26px rgb(var(--shadow-color) / 7%); } + .pane + .pane { border-left: 1px solid var(--border-soft); } + .pane-title { height: 54px; padding: 0 17px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--border-soft); background: var(--surface-alt); } .pane-title h2 { margin: 0; font-size: 14px; } .pane-title span { color: var(--text-muted); font-size: 12px; } .pane-list { padding: 10px; } .pane-list form { margin: 0; } - .browser-item { width: 100%; display: grid; gap: 3px; padding: 11px 12px; text-align: left; border: 1px solid transparent; border-radius: 7px; color: #253246; background: transparent; cursor: pointer; } - .browser-item:hover { background: #f2f6fc; } - .browser-item.selected { border-color: var(--select-border); background: #eaf2ff; } - .browser-item small, .column small { color: #788495; } - .column { display: grid; grid-template-columns: minmax(100px, 1fr) auto; gap: 3px 12px; padding: 10px 11px; border-bottom: 1px solid #edf0f3; } - .column code { color: #315078; font-size: 12px; } + .browser-item { width: 100%; display: grid; gap: 3px; padding: 11px 12px; text-align: left; border: 1px solid transparent; border-radius: 7px; color: var(--text-secondary); background: transparent; cursor: pointer; } + .browser-item:hover { background: var(--hover-bg); } + .browser-item.selected { border-color: var(--select-border); background: var(--info-bg); } + .browser-item small, .column small { color: var(--text-muted); } + .column { display: grid; grid-template-columns: minmax(100px, 1fr) auto; gap: 3px 12px; padding: 10px 11px; border-bottom: 1px solid var(--border-soft); } + .column code { color: var(--code); font-size: 12px; } .column small { grid-column: 1 / -1; } - .column.system { color: #6b7686; background: var(--surface-alt); } - .column.system code { color: #67788e; } + .column.system { color: var(--text-muted); background: var(--surface-alt); } + .column.system code { color: var(--text-muted); } .group-label { margin: 14px 0 4px; padding: 0 11px; color: var(--text-muted); font-size: 11px; letter-spacing: .04em; text-transform: uppercase; } /* What can be done to whatever the pane is pointed at, rendered directly under it: under the selected table, and under the tables list for the profile-wide actions. Each link is a page of its own. */ .browser-actions { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 12px 12px; } - .browser-actions a { padding: 6px 12px; border: 1px solid var(--select-border); border-radius: 7px; color: #1d4ed8; background: #eaf2ff; font-size: 13px; font-weight: 600; text-decoration: none; } - .browser-actions a:hover { background: #d8e8ff; } + .browser-actions a { padding: 6px 12px; border: 1px solid var(--select-border); border-radius: 7px; color: var(--primary-strong); background: var(--info-bg); font-size: 13px; font-weight: 600; text-decoration: none; } + .browser-actions a:hover { background: var(--info-hover-bg); } .browser-actions a.danger-action { border-color: var(--danger-border); color: var(--danger); background: var(--danger-bg); } - .browser-actions a.danger-action:hover { background: #fbe6e6; } - .pane-footer { margin: 0 10px 10px; border-top: 1px solid #e5e8ed; padding-top: 12px; } + .browser-actions a.danger-action:hover { background: var(--danger-hover-bg); } + .pane-footer { margin: 0 10px 10px; border-top: 1px solid var(--border-soft); padding-top: 12px; } .pane-footer a { border-color: var(--border); color: var(--text); background: var(--surface); font-weight: 500; } - .pane-footer a:hover { background: #f2f6fc; } + .pane-footer a:hover { background: var(--hover-bg); } .empty { margin: 8px; color: var(--text-muted); } /* ---------- Forms (components/form_card.html) ---------- */ .form-main { width: min(900px, calc(100% - 32px)); } - .back-link { display: inline-block; margin-bottom: 14px; color: #315f9d; text-decoration: none; } - .form-card { padding: 25px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface); box-shadow: 0 10px 26px rgb(31 43 58 / 7%); } + .back-link { display: inline-block; margin-bottom: 14px; color: var(--link); text-decoration: none; } + .form-card { padding: 25px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface); box-shadow: 0 10px 26px rgb(var(--shadow-color) / 7%); } .form-card h1 { margin: 0; } - .form-card > p { color: #687384; } + .form-card > p { color: var(--text-subtle); } .form-grid { margin-top: 22px; display: grid; grid-template-columns: 1fr 1fr; gap: 17px; } .form-grid label { display: grid; align-content: start; gap: 6px; color: var(--label); font-size: 12px; } .form-grid label.wide { grid-column: 1 / -1; } @@ -156,8 +212,8 @@ .form-grid .field-note { display: grid; align-content: start; gap: 6px; color: var(--label); font-size: 12px; } .form-grid .field-note p { margin: 0; } .form-actions { margin-top: 20px; display: flex; justify-content: end; align-items: center; gap: 14px; } - .form-actions a { color: #59677a; } - .form-actions button { padding: 9px 16px; border: 0; border-radius: 6px; color: white; background: #2563eb; cursor: pointer; } + .form-actions a { color: var(--text-secondary); } + .form-actions button { padding: 9px 16px; border: 0; border-radius: 6px; color: var(--on-primary); background: var(--primary); cursor: pointer; } .check-group { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 12px 20px; } .check { display: flex !important; grid-template-columns: auto 1fr; align-items: center; gap: 6px; } .check input { width: auto; } @@ -167,27 +223,27 @@ /* The alert cards themselves are Penguin UI now — see ui/alert.html. What is left here is the plain coloured text that ui/notice.html and the error page use. */ - .error { color: #b42318; } - .success { color: #067647; } + .error { color: var(--error); } + .success { color: var(--success-strong); } .hint, .result-meta { color: var(--text-subtle); } - .error-page { width: min(600px, calc(100% - 32px)); margin: 15vh auto; padding: 24px; border: 1px solid #f1c4c0; border-radius: 10px; background: var(--surface); } + .error-page { width: min(600px, calc(100% - 32px)); margin: 15vh auto; padding: 24px; border: 1px solid var(--error-border); border-radius: 10px; background: var(--surface); } .error-page h1 { margin-top: 0; } - .error-page p { color: #a33a31; } + .error-page p { color: var(--danger); } /* ---------- Login and register (pages/login, pages/register) ---------- */ .login-main { width: min(420px, calc(100% - 32px)); margin: 12vh auto; } - .login-card { padding: 25px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface); box-shadow: 0 10px 26px rgb(31 43 58 / 7%); } + .login-card { padding: 25px; border: 1px solid var(--border); border-radius: 11px; background: var(--surface); box-shadow: 0 10px 26px rgb(var(--shadow-color) / 7%); } .login-card h1 { margin: 0 0 18px; font-size: 24px; } .login-card label { display: grid; gap: 5px; margin-bottom: 14px; color: var(--label); font-size: 12px; } - .login-card button { width: 100%; border: 0; border-radius: 6px; padding: 10px 18px; color: white; background: #2563eb; cursor: pointer; } + .login-card button { width: 100%; border: 0; border-radius: 6px; padding: 10px 18px; color: var(--on-primary); background: var(--primary); cursor: pointer; } .login-card button:disabled, .login-card button.htmx-request { opacity: .55; cursor: wait; } .login-alt { margin: 14px 0 0; color: var(--text-subtle); font-size: 12px; text-align: center; } /* ---------- Analytics (pages/analytics.html) ---------- */ .analytics { display: grid; grid-template-columns: minmax(290px, 380px) minmax(0, 1fr); align-items: start; gap: 16px; } - .panel { margin-bottom: 16px; padding: 18px; border: 1px solid #dfe4ea; border-radius: 10px; background: var(--surface); } + .panel { margin-bottom: 16px; padding: 18px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); } .panel h2 { margin: 0 0 14px; font-size: 17px; } .sidebar { position: sticky; top: 16px; max-height: calc(100vh - 32px); overflow: auto; } .sidebar h2 { margin-bottom: 6px; } @@ -197,7 +253,7 @@ .catalog-table { border-top: 1px solid var(--border-soft); padding: 9px 0; } .catalog-table summary { display: flex; align-items: center; gap: 8px; cursor: pointer; } .currency { color: var(--text-subtle); font-size: 11px; } - .starter-query, .secondary { margin-top: 9px; padding: 6px 9px; width: auto; border: 0; border-radius: 6px; color: #344054; background: #eef2f6; cursor: pointer; } + .starter-query, .secondary { margin-top: 9px; padding: 6px 9px; width: auto; border: 0; border-radius: 6px; color: var(--text-secondary); background: var(--secondary-bg); cursor: pointer; } .columns, .links ul { margin: 8px 0; padding: 0; list-style: none; } .columns li { display: flex; justify-content: space-between; gap: 8px; padding: 3px 0; } .columns li span { color: var(--text-subtle); font-size: 11px; text-align: right; } @@ -211,42 +267,42 @@ .query-form label { display: grid; gap: 5px; color: var(--label); font-size: 12px; } .query-form textarea { min-height: 130px; } .query-form .actions { margin-top: 12px; align-items: center; gap: 12px; } - .query-form button { border: 0; border-radius: 6px; padding: 10px 18px; width: auto; color: white; background: #2563eb; cursor: pointer; } + .query-form button { border: 0; border-radius: 6px; padding: 10px 18px; width: auto; color: var(--on-primary); background: var(--primary); cursor: pointer; } .query-form button:disabled, .htmx-request button, button.htmx-request { opacity: .55; cursor: wait; } .chart { height: 560px; } .table-wrap { max-height: 560px; overflow: auto; } table { width: 100%; border-collapse: collapse; background: var(--surface); } th, td { padding: 9px 11px; border: 1px solid var(--border-soft); text-align: left; white-space: nowrap; } - th { position: sticky; top: 0; background: #f9fafb; } + th { position: sticky; top: 0; background: var(--surface-alt); } /* ---------- Table builder (Add table) ---------- */ - .builder-section { margin-top: 26px; padding-top: 20px; border-top: 1px solid #e8ebf0; } + .builder-section { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--border-soft); } .builder-section:first-of-type { margin-top: 0; padding-top: 0; border-top: 0; } .builder-section h2 { margin: 0; font-size: 15px; color: var(--text); } .builder-section > .hint { margin: 8px 0 0; font-size: 13px; } - .builder-section .count { margin-left: 6px; padding: 1px 7px; border-radius: 9px; font-size: 12px; color: #4b5563; background: #eef1f6; } + .builder-section .count { margin-left: 6px; padding: 1px 7px; border-radius: 9px; font-size: 12px; color: var(--text-muted); background: var(--secondary-bg); } .builder-section > button.secondary { margin-top: 16px; padding: 9px 16px; border: 1px solid var(--secondary-border); border-radius: 6px; color: var(--text-secondary); background: var(--secondary-bg); cursor: pointer; } .builder-section > button.secondary:hover { background: var(--hover-bg); } .builder-table { margin-top: 14px; } - .builder-table th { position: static; font-size: 12px; color: #5b6678; } + .builder-table th { position: static; font-size: 12px; color: var(--text-muted); } .builder-table td { font-size: 13px; } - .builder-table .mark { width: 34px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: #4a5568; } - .builder-table.preview .source-system td { color: #7a8496; background: #fbfcfd; } - .builder-table.preview .source-relation td { color: #3c5a86; } + .builder-table .mark { width: 34px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-muted); } + .builder-table.preview .source-system td { color: var(--text-muted); background: var(--surface-alt); } + .builder-table.preview .source-relation td { color: var(--code); } /* A compound column's companions, in both tables: the server's columns, so they read as a block hanging off the definition row above them. */ - .builder-table .generated-row td, .builder-table.preview .source-generated td { color: #5b6678; background: #fbfcfd; } + .builder-table .generated-row td, .builder-table.preview .source-generated td { color: var(--text-muted); background: var(--surface-alt); } .builder-table .order { width: 78px; white-space: nowrap; } .builder-table .order .hint { padding-left: 12px; } button.toggle.move { min-width: 26px; padding: 3px 5px; } button.toggle.move + button.toggle.move { margin-left: 4px; } - button.toggle:disabled { color: #b3bcc9; background: #f7f8fa; cursor: default; } + button.toggle:disabled { color: var(--disabled-text); background: var(--disabled-bg); cursor: default; } /* What a compound choice will add, shown before it is added. */ - .compound-note { margin-top: 14px; padding: 12px 14px; border: 1px solid #dfe6f3; border-radius: 8px; background: #f7f9fe; } + .compound-note { margin-top: 14px; padding: 12px 14px; border: 1px solid var(--info-border); border-radius: 8px; background: var(--info-bg); } .compound-note p { margin: 0; font-size: 13px; color: var(--text); } .compound-note ul { margin: 8px 0; padding-left: 18px; font-size: 13px; } .compound-note li { padding: 1px 0; } @@ -255,25 +311,25 @@ /* Renaming what a compound row generates: closed by default, because the backend's names are the answer most tables keep. */ .rename-generated > summary { font-size: 15px; color: var(--text); cursor: pointer; } - .rename-generated > summary::marker { color: #8a94a6; } + .rename-generated > summary::marker { color: var(--text-muted); } .rename-generated > .hint { margin: 8px 0 0; font-size: 13px; } .rename-list { margin: 12px 0 0; padding: 0; list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 8px; } - .rename-list li { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border: 1px solid #e1e6ee; border-radius: 7px; background: var(--surface); } + .rename-list li { display: flex; align-items: center; gap: 8px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface); } .rename-list li > code { flex: none; } .rename-list li > .hint { flex: none; font-size: 12px; } .rename-list input { flex: 1; min-width: 90px; } .builder-list { margin: 12px 0 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 8px; } - .builder-list li { display: flex; align-items: center; gap: 8px; padding: 5px 10px; border: 1px solid #e1e6ee; border-radius: 7px; background: var(--surface); } + .builder-list li { display: flex; align-items: center; gap: 8px; padding: 5px 10px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface); } - button.toggle { min-width: 34px; padding: 3px 7px; border: 1px solid #cdd5e0; border-radius: 5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--text-secondary); background: #f6f8fb; cursor: pointer; } + button.toggle { min-width: 34px; padding: 3px 7px; border: 1px solid var(--border); border-radius: 5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--text-secondary); background: var(--secondary-bg); cursor: pointer; } button.toggle:hover { background: var(--hover-bg); } - button.toggle.mode-optional { color: #1d4ed8; border-color: #bfd0f5; background: #eef3fe; } - button.toggle.mode-required { color: #a1410a; border-color: #f0cfae; background: #fdf3e9; } + button.toggle.mode-optional { color: var(--primary-strong); border-color: var(--select-border); background: var(--info-bg); } + button.toggle.mode-required { color: var(--warning); border-color: var(--warning-border); background: var(--warning-bg); } button.danger { padding: 4px 10px; border: 1px solid var(--danger-border); border-radius: 5px; font-size: 12px; color: var(--danger); background: var(--danger-bg); cursor: pointer; } - button.danger:hover { background: #f8e5e5; } + button.danger:hover { background: var(--danger-hover-bg); } - .tag { display: inline-block; padding: 1px 7px; border-radius: 9px; font-size: 11px; color: #4b5563; background: #eef1f6; } + .tag { display: inline-block; padding: 1px 7px; border-radius: 9px; font-size: 11px; color: var(--text-muted); background: var(--secondary-bg); } .tag + .tag { margin-left: 4px; } /* ---------- Table definition workspace ---------- */ @@ -284,48 +340,48 @@ .panel-actions { margin-top: 16px; } .panel .form-grid { margin-top: 14px; } .panel .form-actions { margin-top: 16px; } - .panel .count { margin-left: 6px; padding: 1px 7px; border-radius: 9px; font-size: 12px; color: #4b5563; background: #eef1f6; } + .panel .count { margin-left: 6px; padding: 1px 7px; border-radius: 9px; font-size: 12px; color: var(--text-muted); background: var(--secondary-bg); } .panel > button.secondary { margin-top: 16px; padding: 9px 16px; border: 1px solid var(--secondary-border); border-radius: 6px; color: var(--text-secondary); background: var(--secondary-bg); cursor: pointer; } .panel > button.secondary:hover { background: var(--hover-bg); } .presentation-layout { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(320px, .9fr); align-items: start; gap: 16px; } .presentation-panel > .hint { min-height: 40px; margin: -7px 0 15px; } - .alias-list { overflow: hidden; border: 1px solid #e2e7ee; border-radius: 8px; background: var(--surface); } + .alias-list { overflow: hidden; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); } .alias-list-head, .alias-row { display: grid; grid-template-columns: minmax(90px, .8fr) 20px minmax(120px, 1.2fr) auto; align-items: center; gap: 8px; } - .alias-list-head { padding: 8px 10px 6px; color: #7b8797; background: #f7f9fb; font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; } - .alias-row { margin: 0; padding: 8px 10px; border-top: 1px solid #e8ecf1; } + .alias-list-head { padding: 8px 10px 6px; color: var(--text-muted); background: var(--surface-alt); font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; } + .alias-row { margin: 0; padding: 8px 10px; border-top: 1px solid var(--border-soft); } .alias-current { min-width: 0; overflow: hidden; color: var(--text); font-size: 13px; text-overflow: ellipsis; } - .alias-arrow { color: #9aa5b4; text-align: center; } + .alias-arrow { color: var(--text-muted); text-align: center; } .alias-row > input { min-width: 0; color: var(--text-secondary); font-size: 13px; font-weight: 500; } - .alias-row > input:focus { border-color: #6ea1ed; outline: 3px solid rgb(37 99 235 / 12%); } - .alias-row > button { padding: 9px 14px; border: 1px solid #b9cdf0; border-radius: 6px; color: #1d4ed8; background: #eef4ff; font-weight: 600; cursor: pointer; } - .alias-row > button:hover { border-color: #8eb1eb; background: #deebff; } - .alias-row > button:focus-visible { outline: 3px solid rgb(37 99 235 / 18%); outline-offset: 2px; } + .alias-row > input:focus { border-color: var(--select-border); outline: 3px solid rgb(var(--focus-color) / 12%); } + .alias-row > button { padding: 9px 14px; border: 1px solid var(--select-border); border-radius: 6px; color: var(--primary-strong); background: var(--info-bg); font-weight: 600; cursor: pointer; } + .alias-row > button:hover { border-color: var(--select-border); background: var(--info-hover-bg); } + .alias-row > button:focus-visible { outline: 3px solid rgb(var(--focus-color) / 18%); outline-offset: 2px; } .alias-row-locked { background: var(--surface-alt); } .alias-row-locked .locked-label { grid-column: 3 / -1; } - .locked-label { color: #8a94a4; font-size: 12px; } + .locked-label { color: var(--text-muted); font-size: 12px; } .column-order { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; } - .column-order li { display: grid; grid-template-columns: 32px 30px minmax(0, 1fr); align-items: center; gap: 8px; min-height: 52px; padding: 8px 12px 8px 8px; border: 1px solid #e2e7ee; border-radius: 8px; background: var(--surface-alt); transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease, opacity 120ms ease; } - .column-order li:hover { border-color: #b8cae3; box-shadow: 0 3px 10px rgb(31 43 58 / 7%); } - .column-order li.is-dragging { z-index: 1; border-color: #7fa5df; box-shadow: 0 9px 22px rgb(31 43 58 / 18%); opacity: .82; transform: scale(1.015); } - .drag-handle { display: grid; place-items: center; width: 32px; height: 34px; padding: 0; border: 0; border-radius: 6px; color: #8b97a8; background: transparent; font-size: 22px; line-height: 1; cursor: grab; touch-action: none; user-select: none; } - .drag-handle:hover { color: #315f9d; background: #e9f0fb; } - .drag-handle:focus-visible { color: var(--primary-strong); outline: 3px solid rgb(37 99 235 / 18%); } + .column-order li { display: grid; grid-template-columns: 32px 30px minmax(0, 1fr); align-items: center; gap: 8px; min-height: 52px; padding: 8px 12px 8px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-alt); transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease, opacity 120ms ease; } + .column-order li:hover { border-color: var(--select-border); box-shadow: 0 3px 10px rgb(var(--shadow-color) / 7%); } + .column-order li.is-dragging { z-index: 1; border-color: var(--select-border); box-shadow: 0 9px 22px rgb(var(--shadow-color) / 18%); opacity: .82; transform: scale(1.015); } + .drag-handle { display: grid; place-items: center; width: 32px; height: 34px; padding: 0; border: 0; border-radius: 6px; color: var(--text-muted); background: transparent; font-size: 22px; line-height: 1; cursor: grab; touch-action: none; user-select: none; } + .drag-handle:hover { color: var(--link); background: var(--info-bg); } + .drag-handle:focus-visible { color: var(--primary-strong); outline: 3px solid rgb(var(--focus-color) / 18%); } .drag-handle:active { cursor: grabbing; } - .order-position { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; color: #64748b; background: #e9eef5; font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; } + .order-position { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 50%; color: var(--text-muted); background: var(--secondary-bg); font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; } .order-name { min-width: 0; overflow: hidden; color: var(--text); font-size: 13px; text-overflow: ellipsis; } - .order-actions { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 8px; margin-top: 14px; padding-top: 14px; border-top: 1px solid #e5e9ef; } - .order-status { color: #788495; font-size: 12px; } - .order-editor.is-dirty .order-status { color: #9a5a08; font-weight: 600; } - .order-actions button { margin: 0; padding: 8px 13px; border: 0; border-radius: 6px; color: white; background: #2563eb; font-weight: 600; cursor: pointer; } - .order-actions button.secondary { border: 1px solid var(--secondary-border); color: #344054; background: var(--surface); } - .order-actions button:hover:not(:disabled) { background: #1d4ed8; } - .order-actions button.secondary:hover:not(:disabled) { background: #eef2f6; } - .order-actions button:focus-visible { outline: 3px solid rgb(37 99 235 / 18%); outline-offset: 2px; } - .order-actions button:disabled { border-color: #e1e6ed; color: #adb6c3; background: #f3f5f7; cursor: default; } + .order-actions { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: center; gap: 8px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-soft); } + .order-status { color: var(--text-muted); font-size: 12px; } + .order-editor.is-dirty .order-status { color: var(--warning); font-weight: 600; } + .order-actions button { margin: 0; padding: 8px 13px; border: 0; border-radius: 6px; color: var(--on-primary); background: var(--primary); font-weight: 600; cursor: pointer; } + .order-actions button.secondary { border: 1px solid var(--secondary-border); color: var(--text-secondary); background: var(--surface); } + .order-actions button:hover:not(:disabled) { background: var(--primary-strong); } + .order-actions button.secondary:hover:not(:disabled) { background: var(--secondary-bg); } + .order-actions button:focus-visible { outline: 3px solid rgb(var(--focus-color) / 18%); outline-offset: 2px; } + .order-actions button:disabled { border-color: var(--border); color: var(--disabled-text); background: var(--disabled-bg); cursor: default; } .sql-preview { margin: 12px 0 0; padding: 12px; max-height: 260px; overflow: auto; border: 1px solid var(--border-soft); border-radius: 8px; font: 12px/1.5 ui-monospace, monospace; color: var(--text); background: var(--surface-alt); white-space: pre-wrap; } .danger-panel { border-color: var(--danger-border); } .danger-panel h2 { color: var(--danger); } - .form-actions button.danger-submit { background: #a12b2b; } + .form-actions button.danger-submit { color: var(--on-danger); background: var(--danger); } /* ---------- Permissions (pages/permissions) ---------- */ @@ -337,8 +393,8 @@ .tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; } .tab { display: grid; gap: 2px; padding: 10px 16px; border: 1px solid var(--border); border-radius: 9px; color: var(--text); background: var(--surface); text-decoration: none; } .tab small { color: var(--text-muted); font-size: 11px; } - .tab:hover { background: #f2f6fc; } - .tab.selected { border-color: var(--select-border); background: #eaf2ff; } + .tab:hover { background: var(--hover-bg); } + .tab.selected { border-color: var(--select-border); background: var(--info-bg); } .tab.selected span { font-weight: 700; color: var(--primary-strong); } /* ---------- Exchange rates (pages/admin/ecb) ---------- */ @@ -353,7 +409,7 @@ /* The postable limits. This is what the reader came for -- coverage dates are the mechanism behind it -- so it sits directly under the headline and is the only thing on the card set at reading size. */ - .postable { margin-top: 16px; padding: 14px; border: 1px solid #dfe4ea; border-radius: 8px; background: var(--surface); } + .postable { margin-top: 16px; padding: 14px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); } .postable-lead { margin: 0 0 10px; color: var(--text-muted); font-size: 11px; letter-spacing: .04em; text-transform: uppercase; } .postable-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; margin: 0 0 10px; } .postable-grid dt { color: var(--text); font-size: 13px; font-weight: 600; } @@ -363,7 +419,7 @@ /* Coverage reads left to right as one sentence -- reached, gap, owed -- so the two dates are compared rather than looked up one at a time. */ - .coverage { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 16px; padding: 12px 14px; border-radius: 8px; background: #f7f9fc; } + .coverage { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 16px; padding: 12px 14px; border-radius: 8px; background: var(--surface-alt); } .coverage-point { display: flex; flex-direction: column; gap: 2px; } .coverage-label { color: var(--text-muted); font-size: 11px; letter-spacing: .04em; text-transform: uppercase; } .coverage-date { color: var(--text-strong); font-size: 20px; font-variant-numeric: tabular-nums; } @@ -372,8 +428,8 @@ .status-grid dt { color: var(--text-muted); font-size: 11px; letter-spacing: .04em; text-transform: uppercase; } .status-grid dd { margin: 3px 0 0; color: var(--text-strong); font-size: 15px; font-variant-numeric: tabular-nums; } .currency-list { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 14px; } - .failure-note { margin-top: 14px; padding-top: 12px; border-top: 1px solid #edf0f3; } - .tag-ok { color: var(--success); background: #f2f9f3; border-color: #cfe0c4; } + .failure-note { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-soft); } + .tag-ok { color: var(--success); background: var(--success-bg); border-color: var(--success-border); } .tag-bad { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-border); } /* Two facts per cell, so the second sits under the first rather than stretching the column. */ @@ -386,12 +442,12 @@ /* The endpoint is the same long URL on nearly every row: kept for evidence, truncated so it cannot set the column width, full text in the tooltip. */ .endpoint { display: block; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .attempt-failed td { background: #fdf7f7; } - .attempt-error td { padding-top: 0; color: var(--danger); font-size: 12px; background: #fdf7f7; border-top: 0; } + .attempt-failed td { background: var(--danger-bg); } + .attempt-error td { padding-top: 0; color: var(--danger); font-size: 12px; background: var(--danger-bg); border-top: 0; } .table-scroll { overflow-x: auto; } .link-action { color: var(--primary); text-decoration: none; } - .notice { padding: 10px 12px; border: 1px solid #cfe0c4; border-radius: 8px; color: var(--success); background: #f2f9f3; } + .notice { padding: 10px 12px; border: 1px solid var(--success-border); border-radius: 8px; color: var(--success); background: var(--success-bg); } /* The shortcut buttons — read everything, full access, clear — sit above the matrix they fill in, so they are wider than the cell buttons and read as @@ -405,16 +461,16 @@ .grant-matrix th { text-transform: capitalize; } .grant-matrix .object-label { display: block; font-weight: 600; color: var(--text-secondary); } .grant-matrix td small { display: block; margin: 2px 0; color: var(--text-muted); } - .grant-matrix td code { font-size: 11px; color: #5b6678; } + .grant-matrix td code { font-size: 11px; color: var(--text-muted); } .grant-cell { width: 82px; text-align: center; vertical-align: middle; } .grant-cell .actions { justify-content: center; gap: 4px; } .grant-cell form { margin: 0 auto; } .grant-cell button { min-width: 34px; padding: 4px 9px; border-radius: 5px; cursor: pointer; } - button.cell-on { border: 1px solid #a9d3b4; color: var(--success); background: #eefaf1; } - button.cell-on:hover { border-color: #e0b7b3; color: var(--danger); background: var(--danger-bg); } - button.cell-off { border: 1px dashed #cdd5e0; color: #8a94a4; background: var(--surface); } - button.cell-off:hover { border-style: solid; border-color: var(--select-border); color: #1d4ed8; background: #eef3fe; } - .cell-closed { color: #c3cad4; } + button.cell-on { border: 1px solid var(--success-border); color: var(--success); background: var(--success-bg); } + button.cell-on:hover { border-color: var(--danger-border); color: var(--danger); background: var(--danger-bg); } + button.cell-off { border: 1px dashed var(--border); color: var(--text-muted); background: var(--surface); } + button.cell-off:hover { border-style: solid; border-color: var(--select-border); color: var(--primary-strong); background: var(--info-bg); } + .cell-closed { color: var(--disabled-text); } .cell-inherited { color: var(--primary-strong); } /* ---------- A running import (import/progress.html) ---------- */ @@ -423,17 +479,17 @@ sits where the alert would and carries the same weight as one. Every number is tabular: the figures update every second, and digits that change width make the whole line jitter. */ - .import-progress { margin: 14px 0; padding: 14px 16px; border: 1px solid #cfdcef; border-radius: 9px; background: #f7faff; } + .import-progress { margin: 14px 0; padding: 14px 16px; border: 1px solid var(--info-border); border-radius: 9px; background: var(--info-bg); } .import-progress h3 { margin: 0 0 10px; color: var(--text-secondary); font-size: 14px; } .progress-line { display: flex; align-items: center; gap: 10px; } - .import-progress progress { flex: 1; width: 100%; height: 10px; border: 0; border-radius: 999px; background: #dde6f2; overflow: hidden; } - .import-progress progress::-webkit-progress-bar { border-radius: 999px; background: #dde6f2; } - .import-progress progress::-webkit-progress-value { border-radius: 999px; background: #3b6fc4; transition: width 400ms ease; } - .import-progress progress::-moz-progress-bar { border-radius: 999px; background: #3b6fc4; } + .import-progress progress { flex: 1; width: 100%; height: 10px; border: 0; border-radius: 999px; background: var(--border-soft); overflow: hidden; } + .import-progress progress::-webkit-progress-bar { border-radius: 999px; background: var(--border-soft); } + .import-progress progress::-webkit-progress-value { border-radius: 999px; background: var(--primary); transition: width 400ms ease; } + .import-progress progress::-moz-progress-bar { border-radius: 999px; background: var(--primary); } .progress-percent { min-width: 42px; color: var(--text-secondary); font-size: 13px; font-variant-numeric: tabular-nums; text-align: right; } .progress-rows { margin: 8px 0 0; color: var(--text); font-size: 13px; font-variant-numeric: tabular-nums; } .progress-stats { display: flex; flex-wrap: wrap; gap: 6px 24px; margin: 10px 0 0; } - .progress-stats dt { color: #6d798a; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; } + .progress-stats dt { color: var(--text-muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; } .progress-stats dd { margin: 2px 0 0; color: var(--text-secondary); font-size: 14px; font-variant-numeric: tabular-nums; } .import-progress .hint { margin: 10px 0 0; font-size: 12px; } @@ -442,64 +498,64 @@ /* CSV columns are a movable palette; table columns stay fixed as the import destinations. A select in every row is the keyboard/touch equivalent of dragging a source chip onto it. */ - .mapping-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin: 14px 0; padding: 10px 12px; border-radius: 8px; background: #f5f7fa; } + .mapping-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin: 14px 0; padding: 10px 12px; border-radius: 8px; background: var(--surface-alt); } .mapping-summary strong { color: var(--text-secondary); font-variant-numeric: tabular-nums; } .mapping-workbench h3 { margin: 0; color: var(--text-secondary); font-size: 14px; } .mapping-workbench h3 + .hint { margin: 4px 0 12px; font-size: 12px; } /* The batch actions stay reachable while a long destination list scrolls. */ - .mapping-toolbar { position: sticky; top: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 12px; padding: 8px; border: 1px solid #d8e1ec; border-radius: 8px; background: rgb(255 255 255 / 96%); box-shadow: 0 3px 12px rgb(31 43 58 / 7%); } + .mapping-toolbar { position: sticky; top: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: 7px; margin-bottom: 12px; padding: 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--toolbar-bg); box-shadow: 0 3px 12px rgb(var(--shadow-color) / 7%); } .mapping-toolbar button.secondary { margin: 0; padding: 6px 10px; border: 1px solid var(--secondary-border); border-radius: 6px; color: var(--text-secondary); background: var(--secondary-bg); font-size: 12px; cursor: pointer; } .mapping-toolbar button.secondary:hover { background: var(--hover-bg); } .mapping-panels { display: grid; grid-template-columns: minmax(240px, .8fr) minmax(460px, 1.5fr); align-items: start; gap: 14px; } - .source-palette, .destination-map { padding: 14px; border: 1px solid #dfe5ed; border-radius: 9px; background: var(--surface-alt); } + .source-palette, .destination-map { padding: 14px; border: 1px solid var(--border); border-radius: 9px; background: var(--surface-alt); } /* The palette follows the destination list down, so a column near the bottom of a wide table still has something to be dropped from. */ .source-palette { position: sticky; top: 56px; max-height: calc(100vh - 76px); overflow: auto; } .palette-heading { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px; } .palette-actions { display: flex; align-items: center; gap: 6px; } - .palette-actions button.secondary { margin: 0; padding: 4px 8px; border: 1px solid #cfd8e5; border-radius: 6px; color: #46566d; background: var(--surface); font-size: 11px; cursor: pointer; } - .selection-count { min-width: 42px; color: #6d798a; font-size: 11px; font-variant-numeric: tabular-nums; text-align: right; } + .palette-actions button.secondary { margin: 0; padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px; color: var(--text-secondary); background: var(--surface); font-size: 11px; cursor: pointer; } + .selection-count { min-width: 42px; color: var(--text-muted); font-size: 11px; font-variant-numeric: tabular-nums; text-align: right; } /* Every track is a fixed width and there is only ever one row, so selecting a chip or mapping it changes colour and nothing else — no part of the list moves under the pointer that just clicked it. */ .source-chips { display: grid; gap: 7px; } - .source-chip { display: grid; grid-template-columns: 19px minmax(0, 1fr) 108px; align-items: center; gap: 8px; width: 100%; padding: 9px 10px; border: 1px solid #cfd9e6; border-radius: 7px; color: var(--text); background: var(--surface); text-align: left; cursor: grab; transition: border-color 120ms ease, box-shadow 120ms ease, opacity 120ms ease; } - .source-chip:hover { border-color: #78a1df; box-shadow: 0 3px 10px rgb(31 43 58 / 9%); } - .source-chip.is-mapped { border-color: #a8cfb3; background: #f4fbf6; } - .source-chip.is-selected { border-color: #78a1df; background: #f3f7ff; outline: 3px solid rgb(37 99 235 / 12%); } + .source-chip { display: grid; grid-template-columns: 19px minmax(0, 1fr) 108px; align-items: center; gap: 8px; width: 100%; padding: 9px 10px; border: 1px solid var(--border); border-radius: 7px; color: var(--text); background: var(--surface); text-align: left; cursor: grab; transition: border-color 120ms ease, box-shadow 120ms ease, opacity 120ms ease; } + .source-chip:hover { border-color: var(--select-border); box-shadow: 0 3px 10px rgb(var(--shadow-color) / 9%); } + .source-chip.is-mapped { border-color: var(--success-border); background: var(--success-bg); } + .source-chip.is-selected { border-color: var(--select-border); background: var(--info-bg); outline: 3px solid rgb(var(--focus-color) / 12%); } .source-chip.is-dragging { opacity: .55; cursor: grabbing; } .source-chip code { overflow: hidden; text-overflow: ellipsis; } /* The rank a selected column will take when the batch is placed. Its track is reserved whether or not it is showing. */ - .chip-order { display: grid; width: 19px; height: 19px; place-items: center; border-radius: 50%; visibility: hidden; color: white; background: #2563eb; font-size: 11px; font-variant-numeric: tabular-nums; } + .chip-order { display: grid; width: 19px; height: 19px; place-items: center; border-radius: 50%; visibility: hidden; color: var(--on-primary); background: var(--primary); font-size: 11px; font-variant-numeric: tabular-nums; } .source-chip.is-selected .chip-order { visibility: visible; } /* One slot, two things to say: the first data value while the column is still free, and where it went once it is mapped — by then the destination row is showing that value anyway. */ .source-chip small, .chip-target { grid-column: 3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .source-chip small { color: #778396; } - .chip-target { color: #287443; font-size: 11px; } + .source-chip small { color: var(--text-muted); } + .chip-target { color: var(--success); font-size: 11px; } .source-chip.is-mapped small, .chip-target:empty { display: none; } .destination-rows { display: grid; gap: 7px; margin: 0; padding: 0; list-style: none; } - .destination-rows li { display: grid; grid-template-columns: minmax(100px, .7fr) 22px minmax(210px, 1.3fr) minmax(70px, .65fr) auto; align-items: center; gap: 8px; min-height: 62px; padding: 8px 9px; border: 1px dashed #cdd6e2; border-radius: 7px; background: var(--surface); transition: border-color 120ms ease, background 120ms ease, box-shadow 120ms ease; } - .destination-rows li.is-mapped { border-style: solid; border-color: #b9cce8; } + .destination-rows li { display: grid; grid-template-columns: minmax(100px, .7fr) 22px minmax(210px, 1.3fr) minmax(70px, .65fr) auto; align-items: center; gap: 8px; min-height: 62px; padding: 8px 9px; border: 1px dashed var(--border); border-radius: 7px; background: var(--surface); transition: border-color 120ms ease, background 120ms ease, box-shadow 120ms ease; } + .destination-rows li.is-mapped { border-style: solid; border-color: var(--select-border); } /* With a selection live, every row is somewhere the batch can be dropped. */ - .mapping-workbench.has-selection .destination-rows li { border-color: #9bbcf0; cursor: copy; } - .destination-rows li.drop-ready { border-style: solid; border-color: var(--primary); background: #edf4ff; box-shadow: 0 0 0 3px rgb(37 99 235 / 12%); } - .destination-rows li.just-mapped { background: #eef8f1; } + .mapping-workbench.has-selection .destination-rows li { border-color: var(--select-border); cursor: copy; } + .destination-rows li.drop-ready { border-style: solid; border-color: var(--primary); background: var(--info-bg); box-shadow: 0 0 0 3px rgb(var(--focus-color) / 12%); } + .destination-rows li.just-mapped { background: var(--success-bg); } .destination-name { display: flex; flex-wrap: wrap; align-items: center; gap: 5px; min-width: 0; } .destination-name code { overflow: hidden; color: var(--text-secondary); text-overflow: ellipsis; } - .mapping-arrow { color: #9aa5b4; text-align: center; } - .destination-rows label { display: grid; gap: 3px; color: #778396; font-size: 10px; letter-spacing: .04em; text-transform: uppercase; cursor: auto; } + .mapping-arrow { color: var(--text-muted); text-align: center; } + .destination-rows label { display: grid; gap: 3px; color: var(--text-muted); font-size: 10px; letter-spacing: .04em; text-transform: uppercase; cursor: auto; } .destination-rows select { min-width: 0; color: var(--text-secondary); font-size: 13px; letter-spacing: normal; text-transform: none; } .mapped-example { min-width: 0; overflow: hidden; color: var(--text-subtle); font: 11px/1.4 ui-monospace, monospace; text-overflow: ellipsis; white-space: nowrap; } - .clear-mapping { padding: 6px 8px; border: 1px solid #d6dde7; border-radius: 6px; color: #59677a; background: var(--surface); font-size: 11px; cursor: pointer; } + .clear-mapping { padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; color: var(--text-secondary); background: var(--surface); font-size: 11px; cursor: pointer; } .clear-mapping:hover:not(:disabled) { color: var(--danger); background: var(--danger-bg); } - .clear-mapping:disabled { color: #bdc4ce; background: #f7f8fa; cursor: default; } + .clear-mapping:disabled { color: var(--disabled-text); background: var(--disabled-bg); cursor: default; } @media (max-width: 850px) { .mapping-panels { grid-template-columns: 1fr; } @@ -520,7 +576,7 @@ @media (max-width: 850px) { .heading { align-items: start; flex-direction: column; } .workspace { grid-template-columns: 1fr; } - .pane + .pane { border-left: 0; border-top: 1px solid #e1e5eb; } + .pane + .pane { border-left: 0; border-top: 1px solid var(--border-soft); } .analytics { grid-template-columns: 1fr; } .sidebar { position: static; max-height: none; } .query-row { grid-template-columns: 1fr; } diff --git a/web/templates/ui/base.html b/web/templates/ui/base.html index fc37b8ca..8b1daf75 100644 --- a/web/templates/ui/base.html +++ b/web/templates/ui/base.html @@ -40,52 +40,41 @@ #}