Files
tanshu 32eeed144f Let the owner change the name, the icon and the dates
Three things that needed a rebuild and a redeploy, or had no way in at all, now
have a Site identity panel in the admin: the restaurant's name and tagline, an
SVG icon upload, and a switch for whether a menu says when it was published.
They live in data/site.json and data/brand/icon.svg, extending ADR 0007: what
the owner changes is content, and the config is the seed plus the theme, which
still needs a rebuild.

An uploaded SVG is a document that would run with this origin's authority, so it
is reduced to shapes on the way in and served with default-src 'none'. The
archive list keeps its dates even when the switch is off, because those dates
are how one old menu is told from another.

Also fixes the live check's own trap: the app caches identity in memory, so the
tests drive it through the app rather than a second store instance, which would
be a second writer.
2026-09-22 23:11:40 +05:30

472 lines
7.3 KiB
CSS

/* The administrative side of the same system: an espresso workspace with warm
obsidian pods and burnished gold for the one active thing. See
docs/design/stitch/atelier-dining-hospitality.md. */
body.admin-page {
--steel: #121110;
--rail: #1e1c1a;
--paper: #faf8f5;
--ink: #faf8f5;
--ink-soft: #c9c4bd;
--ink-dim: #a9a29a;
--ink-on-paper: #121110;
--accent: #c59b58;
--copper: #b08544;
--hairline: #2d2a26;
--hairline-soft: #262320;
--sheet-shadow: rgba(0, 0, 0, 0.45);
--shadow-card: none;
--shadow-float: 0 12px 36px -4px rgba(0, 0, 0, 0.5);
--radius-card: 0.5rem;
--radius-pill: 9999px;
}
body {
font-family: var(--font-ui, 'Plus Jakarta Sans', system-ui, sans-serif);
}
.button {
border-radius: var(--radius-pill);
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.02em;
}
.field input,
.catrow__name input {
min-height: 44px;
border-radius: var(--radius-card);
}
.field input:focus,
.catrow__name input:focus {
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(197, 155, 88, 0.18);
outline: none;
}
.page-admin,
.drop {
border-radius: var(--radius-card);
}
.pill {
border-radius: var(--radius-pill);
letter-spacing: 0.06em;
text-transform: uppercase;
font-size: 0.62rem;
}
/* The admin shares the customer tokens and stays out of the way: forms,
lists, and thumbnails good enough to judge a page by. */
.admin {
max-width: 62rem;
margin: 0 auto;
padding: 1.25rem 1rem 4rem;
}
.admin__bar {
display: flex;
align-items: baseline;
gap: 0.85rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--hairline);
}
.admin__title {
margin: 0;
font-size: var(--step-2);
font-weight: 600;
font-stretch: 116%;
}
.admin__section {
flex: 1 1 auto;
color: var(--ink-dim);
font-size: 0.9rem;
}
.block {
margin-top: 1.75rem;
}
.block__title {
margin: 0 0 0.5rem;
color: var(--ink-dim);
font-size: 0.85rem;
font-weight: 500;
letter-spacing: 0.02em;
}
.setlist {
margin: 0;
padding: 0;
list-style: none;
}
.setlist li {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.75rem;
padding: 0.55rem 0;
border-bottom: 1px solid var(--hairline-soft);
}
.setlist__none {
color: var(--ink-dim);
font-size: 0.9rem;
}
.setlist__main {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 0.5rem;
}
.setlist__label {
font-weight: 500;
}
.setlist__meta {
color: var(--ink-dim);
font-size: 0.85rem;
}
.setlist__actions {
display: flex;
gap: 0.35rem;
white-space: nowrap;
}
.pill {
padding: 0.1rem 0.4rem;
border: 1px solid var(--hairline);
color: var(--ink-dim);
font-size: 0.72rem;
text-transform: none;
}
.pill--current {
border-color: transparent;
background: var(--accent);
color: var(--steel);
font-weight: 600;
}
.pill--draft {
border-color: transparent;
background: var(--copper);
color: var(--steel);
font-weight: 600;
}
.button {
display: inline-block;
padding: 0.4rem 0.7rem;
border: 1px solid var(--hairline);
background: var(--rail);
color: var(--ink);
font: inherit;
font-size: 0.88rem;
cursor: pointer;
}
.button:hover {
border-color: var(--accent);
}
.button--primary {
border-color: transparent;
background: var(--accent);
color: var(--steel);
font-weight: 600;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.button--danger {
border-color: color-mix(in srgb, #d06a5a 55%, transparent);
color: #eeb8ae;
}
.button--icon {
padding: 0.25rem 0.5rem;
font-size: 0.9rem;
}
.newset,
.inline-form {
display: flex;
align-items: flex-end;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 0.9rem;
}
.newset {
padding-top: 0.9rem;
}
.field {
display: flex;
flex-direction: column;
gap: 0.3rem;
min-width: 12rem;
}
.field label {
display: flex;
flex-direction: column;
gap: 0.3rem;
color: var(--ink-dim);
font-size: 0.82rem;
}
.field input {
padding: 0.45rem 0.55rem;
border: 1px solid var(--hairline);
background: var(--steel);
color: var(--ink);
font: inherit;
font-size: 0.95rem;
}
.field__hint,
.muted {
color: var(--ink-dim);
font-size: 0.85rem;
}
.actions {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.5rem;
}
.drop {
padding: 1.1rem;
border: 1px dashed var(--hairline);
color: var(--ink-dim);
text-align: center;
}
.drop.is-over {
border-color: var(--accent);
color: var(--ink);
}
.drop__text {
margin: 0;
}
.drop__hint {
margin: 0.4rem 0 0;
font-size: 0.82rem;
}
.drop__status {
margin: 0.6rem 0 0;
min-height: 1.2em;
color: var(--accent);
font-size: 0.88rem;
}
.no-js-upload {
display: none;
}
.pages-admin {
display: grid;
gap: 0.5rem;
margin: 1rem 0 0;
padding: 0;
list-style: none;
}
.page-admin {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem;
background: var(--rail);
}
.page-admin.is-dragging {
opacity: 0.4;
}
.page-admin img {
display: block;
width: 64px;
height: auto;
background: var(--paper);
}
.page-admin__handle {
color: var(--ink-dim);
cursor: grab;
user-select: none;
}
.page-admin__meta {
display: flex;
flex: 1 1 auto;
flex-direction: column;
gap: 0.15rem;
min-width: 0;
font-size: 0.82rem;
color: var(--ink-dim);
}
.page-admin__number {
color: var(--ink);
font-weight: 600;
}
.page-admin__name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.page-admin__actions {
display: flex;
gap: 0.25rem;
}
.notice--error {
border-left-color: #d06a5a;
background: color-mix(in srgb, #d06a5a 18%, var(--rail));
}
.fold summary {
color: var(--ink-dim);
font-size: 0.85rem;
cursor: pointer;
}
/* Login */
.login {
max-width: 21rem;
margin: 14vh auto 0;
padding: 0 1rem;
}
.login__sub {
margin: 0.2rem 0 1.5rem;
color: var(--ink-dim);
}
.login form {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: flex-start;
}
.login .field {
width: 100%;
}
.login input {
width: 100%;
}
/* ---------- Categories ---------- */
.catlist {
margin: 0.4rem 0 0;
padding: 0;
list-style: none;
}
.catrow {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.4rem 0.6rem;
padding: 0.5rem 0;
border-bottom: 1px solid var(--hairline-soft);
}
.catrow__name {
display: flex;
flex: 1 1 13rem;
gap: 0.4rem;
min-width: 0;
}
.catrow__name input {
flex: 1 1 auto;
min-width: 0;
padding: 0.4rem 0.5rem;
border: 1px solid var(--hairline);
background: var(--steel);
color: var(--ink);
font: inherit;
font-size: 0.95rem;
}
.catrow__url {
color: var(--ink-dim);
font-size: 0.8rem;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.catrow__actions {
display: flex;
gap: 0.25rem;
margin-left: auto;
}
.fold--categories > summary {
color: var(--ink);
font-size: 0.95rem;
}
.fold--categories .muted {
margin: 0.5rem 0 0;
}
/* ---------- Site identity ---------- */
.check {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--ink-soft, var(--ink-dim));
font-size: 0.85rem;
}
.check input[type='checkbox'] {
width: 1.05rem;
height: 1.05rem;
accent-color: var(--accent);
}
.identity__icon {
width: 40px;
height: 40px;
border: 1px solid var(--hairline);
border-radius: var(--radius-pill, 999px);
background: var(--rail);
object-fit: contain;
}
.fold--identity .inline-form {
margin-top: 0.9rem;
}
.fold--identity .inline-form + form {
margin-top: 0.6rem;
}