1326 lines
28 KiB
CSS
1326 lines
28 KiB
CSS
:root {
|
|
--bg: #f6f6f4;
|
|
--fg: #1c1c1a;
|
|
--muted: #6b6b66;
|
|
--card: #ffffff;
|
|
--line: #dedcd6;
|
|
--accent: #2f6f4e;
|
|
--accent-fg: #ffffff;
|
|
--error: #a32a1f;
|
|
--warn-bg: #fdf3e0;
|
|
--warn-line: #e4c98a;
|
|
--radius: 10px;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: #16161a;
|
|
--fg: #e8e8e4;
|
|
--muted: #9a9a94;
|
|
--card: #202027;
|
|
--line: #34343c;
|
|
--accent: #58a37a;
|
|
--accent-fg: #10130f;
|
|
--error: #e88a80;
|
|
--warn-bg: #2e2718;
|
|
--warn-line: #6b5a2e;
|
|
}
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
line-height: 1.55;
|
|
font-size: 16px;
|
|
}
|
|
|
|
#app {
|
|
max-width: 30rem;
|
|
margin: 0 auto;
|
|
padding: 1.5rem 1rem calc(2rem + env(safe-area-inset-bottom));
|
|
}
|
|
|
|
.loading { color: var(--muted); text-align: center; padding: 3rem 0; }
|
|
|
|
.card {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
padding: 1.5rem 1.25rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
h1 { font-size: 1.35rem; margin: 0 0 .5rem; }
|
|
|
|
.lead { color: var(--muted); margin: 0 0 1.25rem; }
|
|
|
|
.lead.warn {
|
|
color: var(--fg);
|
|
background: var(--warn-bg);
|
|
border: 1px solid var(--warn-line);
|
|
border-radius: var(--radius);
|
|
padding: .75rem .9rem;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
font-weight: 600;
|
|
font-size: .9rem;
|
|
margin: 1rem 0 .3rem;
|
|
}
|
|
|
|
.hint { font-weight: 400; color: var(--muted); }
|
|
|
|
input[type="email"],
|
|
input[type="password"],
|
|
input[type="text"] {
|
|
width: 100%;
|
|
padding: .7rem .8rem;
|
|
font-size: 1rem;
|
|
font-family: inherit;
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
input:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
button {
|
|
font: inherit;
|
|
cursor: pointer;
|
|
border-radius: var(--radius);
|
|
border: 1px solid transparent;
|
|
padding: .7rem 1.1rem;
|
|
}
|
|
|
|
button.primary {
|
|
width: 100%;
|
|
margin-top: 1.25rem;
|
|
background: var(--accent);
|
|
color: var(--accent-fg);
|
|
font-weight: 600;
|
|
}
|
|
|
|
button.primary:disabled { opacity: .55; cursor: progress; }
|
|
|
|
.row { display: flex; gap: .5rem; }
|
|
.row input { flex: 1; }
|
|
.row button.primary { width: auto; margin-top: 0; flex-shrink: 0; }
|
|
|
|
.error, .notice {
|
|
margin: .9rem 0 0;
|
|
padding: .6rem .8rem;
|
|
border-radius: var(--radius);
|
|
font-size: .92rem;
|
|
}
|
|
|
|
.error { color: var(--error); border: 1px solid currentColor; }
|
|
.notice { color: var(--accent); border: 1px solid currentColor; }
|
|
|
|
.switch { margin: 1rem 0 0; text-align: center; font-size: .92rem; }
|
|
a { color: var(--accent); }
|
|
|
|
.bar {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
font-size: .92rem;
|
|
}
|
|
|
|
.who { color: var(--muted); overflow-wrap: anywhere; }
|
|
|
|
.lists { list-style: none; margin: 0 0 1rem; padding: 0; }
|
|
|
|
.lists li {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: .75rem;
|
|
padding: .65rem 0;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
.lists li:last-child { border-bottom: none; }
|
|
.lists .meta { color: var(--muted); font-size: .85rem; white-space: nowrap; }
|
|
.empty { color: var(--muted); }
|
|
|
|
.footnote {
|
|
color: var(--muted);
|
|
font-size: .85rem;
|
|
text-align: center;
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
/* ===================================================================
|
|
Phase 3: Listenansicht
|
|
=================================================================== */
|
|
|
|
a.linklike,
|
|
button.linklike {
|
|
background: none;
|
|
border: none;
|
|
padding: 0;
|
|
color: var(--accent);
|
|
font: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
text-decoration: underline;
|
|
text-underline-offset: 2px;
|
|
}
|
|
|
|
button.secondary {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
color: var(--fg);
|
|
font-weight: 600;
|
|
}
|
|
|
|
button.danger {
|
|
background: none;
|
|
border: 1px solid var(--line);
|
|
color: var(--error);
|
|
padding: .4rem .7rem;
|
|
}
|
|
|
|
.offline-hint {
|
|
background: var(--warn-bg);
|
|
border: 1px solid var(--warn-line);
|
|
border-radius: var(--radius);
|
|
padding: .6rem .8rem;
|
|
margin: 0 0 1rem;
|
|
font-size: .9rem;
|
|
}
|
|
|
|
/* --- Artikel hinzufügen --- */
|
|
.add-row { margin-top: .5rem; }
|
|
.add-row > input { margin-bottom: .5rem; }
|
|
.add-row .qty { flex: 0 0 5.5rem; }
|
|
.add-row .unit { flex: 0 0 6rem; }
|
|
|
|
/* --- Markt- und Gruppenabschnitte --- */
|
|
.market {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
margin-bottom: 1rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.market > h2 {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
gap: .75rem;
|
|
margin: 0;
|
|
padding: .8rem 1rem;
|
|
font-size: 1.05rem;
|
|
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
.market-meta {
|
|
font-size: .82rem;
|
|
font-weight: 400;
|
|
color: var(--muted);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.category { padding: 0 0 .25rem; }
|
|
|
|
.category > h3 {
|
|
margin: .75rem 1rem .25rem;
|
|
font-size: .8rem;
|
|
font-weight: 700;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
}
|
|
|
|
ul.items { list-style: none; margin: 0; padding: 0; }
|
|
|
|
li.item { border-top: 1px solid var(--line); }
|
|
.category > ul.items > li.item:first-child { border-top: none; }
|
|
|
|
.item-main {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .6rem;
|
|
padding: .55rem 1rem;
|
|
}
|
|
|
|
/* Große Trefferfläche: die App wird einhändig im Laden bedient.
|
|
padding: 0 ist entscheidend - die allgemeine button-Regel setzt
|
|
1,1rem waagerecht, und das ist mehr als die vorgesehenen 2rem
|
|
Breite. Der Knopf wurde dadurch auseinandergezogen und wirkte oval.
|
|
aspect-ratio hält ihn auch dann kreisrund, wenn eine Schrift die
|
|
Zeilenhöhe verändert. */
|
|
button.check {
|
|
flex: 0 0 2.1rem;
|
|
width: 2.1rem;
|
|
height: 2.1rem;
|
|
aspect-ratio: 1;
|
|
padding: 0;
|
|
border: 2px solid var(--line);
|
|
border-radius: 50%;
|
|
background: var(--bg);
|
|
color: var(--accent-fg);
|
|
font-size: 1.05rem;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
|
|
button.check[aria-pressed="true"] {
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.item-text { flex: 1; min-width: 0; }
|
|
.item-text .name { display: block; overflow-wrap: anywhere; }
|
|
.item-text .sub { display: block; font-size: .82rem; color: var(--muted); }
|
|
|
|
li.item.bought .item-text .name {
|
|
text-decoration: line-through;
|
|
color: var(--muted);
|
|
}
|
|
|
|
li.item.deferred { opacity: .6; }
|
|
li.item.deferred .item-text .name { font-style: italic; }
|
|
|
|
.price-cell {
|
|
flex: 0 0 auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
gap: .1rem;
|
|
}
|
|
|
|
input.price {
|
|
width: 4.5rem;
|
|
padding: .4rem .5rem;
|
|
text-align: right;
|
|
font-size: .9rem;
|
|
}
|
|
|
|
/* Zeilensumme bei mehreren Stück: macht sichtbar, dass der eingetragene
|
|
Preis je Gebinde gilt. */
|
|
.line-total {
|
|
font-size: .75rem;
|
|
color: var(--muted);
|
|
font-variant-numeric: tabular-nums;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
button.menu-toggle {
|
|
flex: 0 0 auto;
|
|
background: none;
|
|
border: none;
|
|
color: var(--muted);
|
|
font-size: 1.2rem;
|
|
line-height: 1;
|
|
padding: .3rem .4rem;
|
|
}
|
|
|
|
.item-menu {
|
|
padding: .25rem 1rem 1rem 3.6rem;
|
|
background: color-mix(in srgb, var(--fg) 4%, transparent);
|
|
}
|
|
|
|
.item-menu label {
|
|
margin: .6rem 0 .2rem;
|
|
font-size: .8rem;
|
|
}
|
|
|
|
.item-menu select {
|
|
width: 100%;
|
|
padding: .5rem;
|
|
font: inherit;
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
.menu-actions {
|
|
display: flex;
|
|
gap: .5rem;
|
|
margin-top: .9rem;
|
|
}
|
|
|
|
.menu-actions button {
|
|
flex: 1;
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
color: var(--fg);
|
|
font-size: .9rem;
|
|
}
|
|
|
|
.menu-actions button.danger { color: var(--error); }
|
|
|
|
/* --- Fußzeile mit Summe --- */
|
|
.summary {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
padding: 1rem;
|
|
}
|
|
|
|
.summary .total {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.summary button { width: 100%; margin-top: .9rem; }
|
|
|
|
/* --- Verwaltung von Märkten und Warengruppen --- */
|
|
|
|
ul.sortable {
|
|
list-style: none;
|
|
margin: 0 0 1.25rem;
|
|
padding: 0;
|
|
}
|
|
|
|
li.sortable-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .4rem;
|
|
padding: .3rem 0;
|
|
background: var(--card);
|
|
/* Beim Ziehen wird transform gesetzt - ohne relative Positionierung
|
|
würde die Zeile hinter ihren Nachbarn verschwinden. */
|
|
position: relative;
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
/* Anfasser: feste Breite, der Rest teilt sich den Platz. */
|
|
button.grip {
|
|
flex: 0 0 2rem;
|
|
height: 2.4rem;
|
|
padding: 0;
|
|
background: none;
|
|
border: 1px solid transparent;
|
|
color: var(--muted);
|
|
font-size: 1.1rem;
|
|
line-height: 1;
|
|
cursor: grab;
|
|
/* Ohne das scrollt die Seite, statt die Zeile zu ziehen. */
|
|
touch-action: none;
|
|
user-select: none;
|
|
}
|
|
|
|
button.grip:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 1px;
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
li.sortable-row input.entry-name {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
button.remove {
|
|
flex: 0 0 2.2rem;
|
|
padding: .45rem 0;
|
|
text-align: center;
|
|
font-size: 1.1rem;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* Während des Ziehens */
|
|
ul.sortable.sorting { cursor: grabbing; }
|
|
ul.sortable.sorting button.grip { cursor: grabbing; }
|
|
ul.sortable.sorting li.sortable-row:not(.dragging) { transition: transform .12s ease; }
|
|
|
|
li.sortable-row.dragging {
|
|
z-index: 2;
|
|
box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
|
|
opacity: .95;
|
|
}
|
|
|
|
li.sortable-row.dragging button.grip { color: var(--accent); }
|
|
|
|
/* --- Druck: A4, gruppiert, mit Kästchen zum Abhaken --- */
|
|
@media print {
|
|
@page { size: A4; margin: 15mm; }
|
|
|
|
body { background: #fff; color: #000; font-size: 11pt; }
|
|
#app { max-width: none; padding: 0; }
|
|
|
|
.bar, .add-row, .summary button, button.menu-toggle,
|
|
.item-menu, .offline-hint, .footnote, label, .row { display: none !important; }
|
|
|
|
.card, .market, .summary {
|
|
border: none;
|
|
background: none;
|
|
padding: 0;
|
|
margin: 0 0 6mm;
|
|
}
|
|
|
|
.market { break-inside: avoid; }
|
|
.market > h2 { background: none; border-bottom: 1pt solid #000; padding: 0 0 1mm; }
|
|
.category > h3 { margin: 3mm 0 1mm; color: #000; }
|
|
|
|
li.item { border-top: none; }
|
|
.item-main { padding: .5mm 0; }
|
|
|
|
/* Leeres Kästchen statt der Schaltfläche */
|
|
button.check {
|
|
width: 4mm; height: 4mm;
|
|
border: .5pt solid #000; border-radius: 0;
|
|
background: none; color: #000; font-size: 8pt;
|
|
}
|
|
|
|
input.price {
|
|
border: none; background: none; width: 18mm;
|
|
border-bottom: .5pt dotted #666;
|
|
}
|
|
}
|
|
|
|
/* ===================================================================
|
|
Phase 5: Teilen
|
|
=================================================================== */
|
|
|
|
.bar-actions { display: flex; gap: 1rem; }
|
|
|
|
select {
|
|
width: 100%;
|
|
padding: .6rem .5rem;
|
|
font: inherit;
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
ul.share-list { list-style: none; margin: 0; padding: 0; }
|
|
|
|
ul.share-list > li {
|
|
padding: .8rem 0;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
ul.share-list > li:last-child { border-bottom: none; }
|
|
|
|
.who-block .name { display: block; font-weight: 600; overflow-wrap: anywhere; }
|
|
.who-block .sub { display: block; font-size: .82rem; color: var(--muted); }
|
|
|
|
select.role { margin-top: .5rem; }
|
|
|
|
.member-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .5rem;
|
|
margin-top: .6rem;
|
|
}
|
|
|
|
.member-actions button {
|
|
flex: 1 1 auto;
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
color: var(--fg);
|
|
font-size: .85rem;
|
|
padding: .45rem .7rem;
|
|
}
|
|
|
|
.member-actions button.danger { color: var(--error); }
|
|
|
|
button.danger.wide {
|
|
width: 100%;
|
|
margin-top: .5rem;
|
|
padding: .7rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
li.invite.revoked .who-block .name,
|
|
li.invite.expired .who-block .name {
|
|
color: var(--muted);
|
|
text-decoration: line-through;
|
|
}
|
|
|
|
li.invite.accepted .who-block .name { color: var(--muted); }
|
|
|
|
@media print {
|
|
.bar-actions, .member-actions, .share-list { display: none !important; }
|
|
}
|
|
|
|
/* ===================================================================
|
|
Öffentliche Ansicht (/s/<token>)
|
|
=================================================================== */
|
|
|
|
.public-head { margin-bottom: 1rem; }
|
|
.public-head h1 { margin-bottom: .2rem; }
|
|
.public-head .sub { margin: 0; color: var(--muted); font-size: .88rem; }
|
|
|
|
span.check.readonly {
|
|
flex: 0 0 2.1rem;
|
|
width: 2.1rem;
|
|
height: 2.1rem;
|
|
aspect-ratio: 1;
|
|
border: 2px solid var(--line);
|
|
border-radius: 50%;
|
|
display: grid;
|
|
place-items: center;
|
|
color: var(--accent);
|
|
font-size: 1.05rem;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.price-static {
|
|
flex: 0 0 auto;
|
|
font-variant-numeric: tabular-nums;
|
|
color: var(--muted);
|
|
font-size: .9rem;
|
|
}
|
|
|
|
label.checkline {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
font-weight: 400;
|
|
margin: .7rem 0 0;
|
|
}
|
|
|
|
label.checkline input[type="checkbox"] {
|
|
width: 1.1rem;
|
|
height: 1.1rem;
|
|
accent-color: var(--accent);
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.fresh-link {
|
|
background: var(--warn-bg);
|
|
border: 1px solid var(--warn-line);
|
|
border-radius: var(--radius);
|
|
padding: .9rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.fresh-link p { margin: 0 0 .5rem; }
|
|
.fresh-link .sub { font-size: .82rem; color: var(--muted); margin: .7rem 0 0; }
|
|
|
|
input.linkfield {
|
|
width: 100%;
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
font-size: .82rem;
|
|
padding: .55rem;
|
|
}
|
|
|
|
input[type="date"] {
|
|
width: 100%;
|
|
padding: .65rem .5rem;
|
|
font: inherit;
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
@media print {
|
|
.public-head .sub, .no-print { display: none !important; }
|
|
span.check.readonly {
|
|
width: 4mm; height: 4mm;
|
|
border: .5pt solid #000; border-radius: 0;
|
|
font-size: 8pt;
|
|
}
|
|
.price-static { color: #000; }
|
|
}
|
|
|
|
/* ===================================================================
|
|
Phase 4: Synchronisationszustand
|
|
=================================================================== */
|
|
|
|
.sync-hint {
|
|
margin: 0 0 1rem;
|
|
padding: .55rem .8rem;
|
|
border-radius: var(--radius);
|
|
font-size: .88rem;
|
|
border: 1px solid;
|
|
}
|
|
|
|
.sync-hint.offline {
|
|
background: var(--warn-bg);
|
|
border-color: var(--warn-line);
|
|
color: var(--fg);
|
|
}
|
|
|
|
.sync-hint.pending {
|
|
border-color: var(--line);
|
|
color: var(--muted);
|
|
}
|
|
|
|
.sync-hint.problem {
|
|
border-color: var(--error);
|
|
color: var(--error);
|
|
}
|
|
|
|
/* Noch nicht bestätigte Änderungen kennzeichnen - ohne sie zu verstecken.
|
|
Der Nutzer soll sehen, dass die Änderung da ist, aber auch, dass sie
|
|
noch unterwegs ist. */
|
|
li.item.pending .item-text .name::after {
|
|
content: " ·";
|
|
color: var(--accent);
|
|
font-weight: 700;
|
|
}
|
|
|
|
li.item.pending { background: color-mix(in srgb, var(--accent) 5%, transparent); }
|
|
|
|
button.check:disabled { opacity: .4; cursor: default; }
|
|
|
|
@media print {
|
|
.sync-hint { display: none !important; }
|
|
li.item.pending { background: none; }
|
|
li.item.pending .item-text .name::after { content: ""; }
|
|
}
|
|
|
|
/* ===================================================================
|
|
Listenübersicht mit Aktionen
|
|
=================================================================== */
|
|
|
|
ul.lists > li.list-row {
|
|
display: block;
|
|
padding: 0;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
ul.lists > li.list-row:last-child { border-bottom: none; }
|
|
|
|
.list-main {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
}
|
|
|
|
button.list-open {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: block;
|
|
text-align: left;
|
|
background: none;
|
|
border: none;
|
|
padding: .7rem 0;
|
|
font: inherit;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
button.list-open .name {
|
|
display: block;
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
button.list-open .meta {
|
|
display: block;
|
|
font-size: .82rem;
|
|
color: var(--muted);
|
|
white-space: normal;
|
|
}
|
|
|
|
.list-menu { padding: 0 0 .8rem; }
|
|
|
|
li.list-row.renaming { padding: .6rem 0; }
|
|
li.list-row.renaming .row { gap: .4rem; }
|
|
li.list-row.renaming input { flex: 1; min-width: 0; }
|
|
|
|
button.cancel {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
color: var(--fg);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* ===================================================================
|
|
Phase 6: Strichcode-Sucher und Artikelstamm
|
|
=================================================================== */
|
|
|
|
body.scanning { overflow: hidden; }
|
|
|
|
.scan-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 100;
|
|
background: #101012;
|
|
color: #f2f2f0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
padding: 1rem calc(1rem + env(safe-area-inset-right))
|
|
calc(1rem + env(safe-area-inset-bottom))
|
|
calc(1rem + env(safe-area-inset-left));
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.scan-stage {
|
|
position: relative;
|
|
flex: 1 1 auto;
|
|
min-height: 40vh;
|
|
border-radius: var(--radius);
|
|
overflow: hidden;
|
|
background: #000;
|
|
}
|
|
|
|
.scan-video {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
}
|
|
|
|
/* Sucherrahmen über dem Bereich, den der Decoder auswertet. */
|
|
.scan-frame {
|
|
position: absolute;
|
|
left: 6%;
|
|
right: 6%;
|
|
top: 32.5%;
|
|
height: 35%;
|
|
border: 2px solid rgba(255, 255, 255, .85);
|
|
border-radius: 6px;
|
|
box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .45);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.scan-hint {
|
|
margin: 0;
|
|
text-align: center;
|
|
font-size: .92rem;
|
|
color: #cfcfc9;
|
|
}
|
|
|
|
.scan-manual label {
|
|
color: #cfcfc9;
|
|
font-size: .85rem;
|
|
margin-bottom: .3rem;
|
|
}
|
|
|
|
.scan-manual input {
|
|
background: #1c1c20;
|
|
border-color: #34343c;
|
|
color: #f2f2f0;
|
|
}
|
|
|
|
.scan-overlay .scan-close {
|
|
background: #1c1c20;
|
|
border-color: #34343c;
|
|
color: #f2f2f0;
|
|
width: 100%;
|
|
}
|
|
|
|
button.secondary.scan { flex-shrink: 0; }
|
|
|
|
/* --- Artikelstamm --- */
|
|
|
|
ul.articles { list-style: none; margin: 1rem 0 0; padding: 0; }
|
|
|
|
li.article-row {
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
li.article-row:last-child { border-bottom: none; }
|
|
|
|
button.article-open {
|
|
display: block;
|
|
width: 100%;
|
|
text-align: left;
|
|
background: none;
|
|
border: none;
|
|
padding: .7rem 0;
|
|
font: inherit;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
button.article-open .name {
|
|
display: block;
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
button.article-open .sub {
|
|
display: block;
|
|
font-size: .82rem;
|
|
color: var(--muted);
|
|
}
|
|
|
|
li.article-row.editing > .name {
|
|
display: block;
|
|
font-weight: 600;
|
|
padding: .7rem 0 0;
|
|
}
|
|
|
|
.article-editor { padding-bottom: 1rem; }
|
|
|
|
.availability { margin-top: 1rem; }
|
|
|
|
.attr-rows { margin-top: .3rem; }
|
|
|
|
.attr-row {
|
|
display: flex;
|
|
gap: .4rem;
|
|
margin-bottom: .4rem;
|
|
}
|
|
|
|
.attr-row .attr-key { flex: 0 0 40%; min-width: 0; }
|
|
.attr-row .attr-value { flex: 1; min-width: 0; }
|
|
|
|
button.linklike.add-attr { margin-top: .2rem; font-size: .9rem; }
|
|
|
|
input[type="search"] {
|
|
width: 100%;
|
|
padding: .7rem .8rem;
|
|
font-size: 1rem;
|
|
font-family: inherit;
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
@media print {
|
|
.scan-overlay { display: none !important; }
|
|
}
|
|
|
|
/* --- Eingabezeile: Name, Detailfelder, Schaltflächen untereinander --- */
|
|
|
|
.add-row > input#add-name { margin-bottom: .5rem; }
|
|
|
|
.add-fields {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .5rem;
|
|
margin-bottom: .5rem;
|
|
}
|
|
|
|
/* Menge schmal, Einheit schmal, Eigenschaft nimmt den Rest. Bei sehr
|
|
schmalen Geräten bricht die Eigenschaft in die nächste Zeile. */
|
|
.add-fields .count { flex: 0 1 4rem; min-width: 3.2rem; text-align: center; }
|
|
.add-fields .qty { flex: 0 1 5rem; min-width: 4rem; }
|
|
.add-fields .unit { flex: 0 1 5.5rem; min-width: 4rem; }
|
|
.add-fields .variant { flex: 1 1 8rem; min-width: 0; }
|
|
|
|
.add-actions {
|
|
display: flex;
|
|
gap: .5rem;
|
|
}
|
|
|
|
/* Vorher liefen die Schaltflächen aus der Box, weil sie sich die Zeile
|
|
mit den Eingabefeldern teilten. Jetzt eigene Zeile, gleichmäßig
|
|
verteilt. */
|
|
.add-actions button {
|
|
flex: 1;
|
|
margin-top: 0;
|
|
}
|
|
|
|
/* ===================================================================
|
|
Bestätigungsdialog nach dem Scan
|
|
=================================================================== */
|
|
|
|
body.modal-open { overflow: hidden; }
|
|
|
|
.modal {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 90;
|
|
background: rgba(0, 0, 0, .5);
|
|
display: flex;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
padding: 1rem;
|
|
padding-bottom: calc(1rem + env(safe-area-inset-bottom));
|
|
overflow-y: auto;
|
|
}
|
|
|
|
@media (min-width: 30rem) {
|
|
.modal { align-items: center; }
|
|
}
|
|
|
|
.modal-card {
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
padding: 1.25rem;
|
|
width: 100%;
|
|
max-width: 28rem;
|
|
}
|
|
|
|
.modal-card h2 { margin: 0 0 .2rem; font-size: 1.15rem; }
|
|
.modal-card .sub { margin: 0 0 1rem; color: var(--muted); font-size: .85rem; }
|
|
|
|
/* Herkunftshinweis: Wenn Angaben aus einer fremden Quelle vorbelegt
|
|
werden, soll das sichtbar sein - nicht als Kleingedrucktes. */
|
|
.source-note {
|
|
background: var(--warn-bg);
|
|
border: 1px solid var(--warn-line);
|
|
border-radius: var(--radius);
|
|
padding: .6rem .8rem;
|
|
margin: 0 0 1rem;
|
|
font-size: .88rem;
|
|
}
|
|
|
|
.modal-card .add-fields { margin-top: .6rem; }
|
|
.modal-card .menu-actions { margin-top: 1.2rem; }
|
|
.modal-card .menu-actions button.primary { margin-top: 0; }
|
|
|
|
/* ===================================================================
|
|
Phase 7: Preisvergleich
|
|
=================================================================== */
|
|
|
|
.price-card { padding: 0; overflow: hidden; }
|
|
|
|
button.price-head {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: baseline;
|
|
gap: .75rem;
|
|
width: 100%;
|
|
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
border: none;
|
|
border-bottom: 1px solid var(--line);
|
|
padding: .75rem 1rem;
|
|
font: inherit;
|
|
color: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
}
|
|
|
|
button.price-head .name { font-weight: 600; overflow-wrap: anywhere; }
|
|
|
|
button.price-head .spread {
|
|
font-size: .8rem;
|
|
color: var(--muted);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.price-rows { padding: .25rem 1rem .75rem; }
|
|
|
|
.price-row {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: .6rem;
|
|
padding: .4rem 0;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
.price-row:last-child { border-bottom: none; }
|
|
.price-row .market { flex: 1; min-width: 0; overflow-wrap: anywhere; }
|
|
|
|
.price-row .amount {
|
|
font-variant-numeric: tabular-nums;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.price-row.best .amount { color: var(--accent); }
|
|
|
|
.price-row .badge {
|
|
flex: 0 0 auto;
|
|
font-size: .72rem;
|
|
font-weight: 700;
|
|
letter-spacing: .03em;
|
|
text-transform: uppercase;
|
|
color: var(--accent);
|
|
border: 1px solid currentColor;
|
|
border-radius: 999px;
|
|
padding: .05rem .45rem;
|
|
}
|
|
|
|
.price-row .diff {
|
|
flex: 0 0 auto;
|
|
font-size: .78rem;
|
|
color: var(--muted);
|
|
font-variant-numeric: tabular-nums;
|
|
min-width: 3.5rem;
|
|
text-align: right;
|
|
}
|
|
|
|
.price-detail {
|
|
padding: .5rem 1rem 1rem;
|
|
border-top: 1px solid var(--line);
|
|
background: color-mix(in srgb, var(--fg) 3%, transparent);
|
|
}
|
|
|
|
.price-detail h4 {
|
|
margin: .6rem 0 .4rem;
|
|
font-size: .8rem;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
}
|
|
|
|
ul.price-markets { list-style: none; margin: 0; padding: 0; }
|
|
|
|
ul.price-markets > li {
|
|
display: grid;
|
|
grid-template-columns: 1fr auto;
|
|
gap: 0 .6rem;
|
|
padding: .45rem 0;
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
|
|
ul.price-markets > li:last-child { border-bottom: none; }
|
|
ul.price-markets .market { font-weight: 600; }
|
|
|
|
ul.price-markets .sub {
|
|
grid-column: 1 / 2;
|
|
font-size: .78rem;
|
|
color: var(--muted);
|
|
}
|
|
|
|
ul.price-markets .amount {
|
|
grid-row: 1 / 3;
|
|
grid-column: 2 / 3;
|
|
align-self: center;
|
|
font-variant-numeric: tabular-nums;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.price-history { margin-top: .8rem; font-size: .85rem; }
|
|
.price-history summary { cursor: pointer; color: var(--accent); }
|
|
.price-history ul { list-style: none; margin: .5rem 0 0; padding: 0; }
|
|
.price-history li { padding: .2rem 0; color: var(--muted); }
|
|
|
|
/* Hinweis im Eintragsmenü */
|
|
.price-tip {
|
|
margin: 0 0 .3rem;
|
|
padding: .5rem .7rem;
|
|
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
border-radius: var(--radius);
|
|
font-size: .85rem;
|
|
}
|
|
|
|
@media print {
|
|
.price-tip, .price-detail { display: none !important; }
|
|
}
|
|
|
|
/* --- Stückzahl direkt am Artikel --- */
|
|
|
|
.item-count {
|
|
flex: 0 0 auto;
|
|
min-width: 1.9rem;
|
|
text-align: right;
|
|
font-variant-numeric: tabular-nums;
|
|
font-size: .95rem;
|
|
color: var(--muted);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Mehr als ein Stück wird hervorgehoben: Beim Einkaufen ist das die
|
|
Angabe, die man leicht übersieht und dann zu wenig einpackt. */
|
|
.item-count.many {
|
|
font-weight: 700;
|
|
color: var(--fg);
|
|
}
|
|
|
|
li.item.bought .item-count { color: var(--muted); font-weight: 400; }
|
|
|
|
@media print {
|
|
.item-count { color: #000; }
|
|
.item-count.many { font-weight: 700; }
|
|
}
|
|
|
|
/* --- Einstellungen --- */
|
|
|
|
h3.device-heading {
|
|
margin: 1.5rem 0 .3rem;
|
|
font-size: .8rem;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
}
|
|
|
|
.card > .menu-actions { margin-top: 1rem; }
|
|
|
|
/* ===================================================================
|
|
Benutzerverwaltung
|
|
=================================================================== */
|
|
|
|
.stats {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: .5rem;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.stat {
|
|
flex: 1 1 5rem;
|
|
min-width: 4.5rem;
|
|
padding: .6rem .5rem;
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
text-align: center;
|
|
}
|
|
|
|
.stat .value {
|
|
display: block;
|
|
font-size: 1.35rem;
|
|
font-weight: 700;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.stat .label { display: block; font-size: .75rem; color: var(--muted); }
|
|
|
|
.stat.warn {
|
|
background: var(--warn-bg);
|
|
border-color: var(--warn-line);
|
|
}
|
|
|
|
ul.admin-users { list-style: none; margin: 0; padding: 0; }
|
|
|
|
li.admin-user { border-bottom: 1px solid var(--line); }
|
|
li.admin-user:last-child { border-bottom: none; }
|
|
|
|
/* Deaktivierte Konten erkennbar, aber nicht versteckt: Man muss sie
|
|
finden, um sie zu reaktivieren. */
|
|
li.admin-user.inactive .user-open .name {
|
|
color: var(--muted);
|
|
text-decoration: line-through;
|
|
}
|
|
|
|
button.user-open {
|
|
display: block;
|
|
width: 100%;
|
|
text-align: left;
|
|
background: none;
|
|
border: none;
|
|
padding: .7rem 0;
|
|
font: inherit;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
button.user-open .name {
|
|
display: block;
|
|
font-weight: 600;
|
|
color: var(--accent);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
button.user-open .sub {
|
|
display: block;
|
|
font-size: .82rem;
|
|
color: var(--muted);
|
|
}
|
|
|
|
li.admin-user .item-menu { padding-left: 0; padding-right: 0; }
|
|
|
|
.item-menu .sub {
|
|
display: block;
|
|
font-size: .82rem;
|
|
color: var(--muted);
|
|
margin: .2rem 0 0;
|
|
}
|
|
|
|
hr.thin {
|
|
border: none;
|
|
border-top: 1px solid var(--line);
|
|
margin: 1.2rem 0 .8rem;
|
|
}
|
|
|
|
input.months {
|
|
width: 6rem;
|
|
padding: .6rem .5rem;
|
|
font: inherit;
|
|
text-align: right;
|
|
color: var(--fg);
|
|
background: var(--bg);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
}
|
|
|
|
@media print { .stats, ul.admin-users { display: none !important; } }
|
|
|
|
/* ===================================================================
|
|
Vorschlagsliste beim Eintippen
|
|
=================================================================== */
|
|
|
|
.name-wrap { position: relative; }
|
|
|
|
ul.suggestions {
|
|
position: absolute;
|
|
z-index: 20;
|
|
left: 0;
|
|
right: 0;
|
|
top: calc(100% + 2px);
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
max-height: 16rem;
|
|
overflow-y: auto;
|
|
background: var(--card);
|
|
border: 1px solid var(--line);
|
|
border-radius: var(--radius);
|
|
box-shadow: 0 6px 18px rgba(0, 0, 0, .16);
|
|
}
|
|
|
|
ul.suggestions > li { border-bottom: 1px solid var(--line); }
|
|
ul.suggestions > li:last-child { border-bottom: none; }
|
|
|
|
ul.suggestions button {
|
|
display: block;
|
|
width: 100%;
|
|
text-align: left;
|
|
background: none;
|
|
border: none;
|
|
padding: .55rem .8rem;
|
|
font: inherit;
|
|
color: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
ul.suggestions button:hover,
|
|
ul.suggestions button:focus-visible {
|
|
background: color-mix(in srgb, var(--accent) 12%, transparent);
|
|
}
|
|
|
|
ul.suggestions .name { display: block; overflow-wrap: anywhere; }
|
|
ul.suggestions .sub { display: block; font-size: .78rem; color: var(--muted); }
|
|
|
|
/* ===================================================================
|
|
Archiv in der Listenübersicht
|
|
=================================================================== */
|
|
|
|
.card.archive { padding-top: .75rem; }
|
|
|
|
button.archive-head {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
width: 100%;
|
|
background: none;
|
|
border: none;
|
|
padding: .4rem 0;
|
|
font: inherit;
|
|
font-weight: 600;
|
|
color: var(--fg);
|
|
cursor: pointer;
|
|
}
|
|
|
|
button.archive-head .chev { color: var(--muted); }
|
|
|
|
.card.archive ul.lists { margin-top: .5rem; }
|
|
|
|
.reg-toggle {
|
|
padding: 0 0 .9rem;
|
|
border-bottom: 1px solid var(--line);
|
|
margin-bottom: .3rem;
|
|
}
|
|
|
|
.reg-toggle .checkline { margin-top: 0; }
|
|
|
|
@media print { ul.suggestions, .card.archive { display: none !important; } }
|