:root { --measure-wide: 1080px; } .admin-sheet { max-width: var(--measure-wide); } .admin-sheet .brand em { color: var(--patina); } .admin-sheet .brand .mark > g > g:last-child { stroke: var(--patina); } .topbar-actions { display: flex; align-items: center; gap: 18px; margin-left: auto; } .navrow { display: flex; gap: 18px; padding: 2px 20px 0; overflow-x: auto; overflow-y: hidden; scrollbar-width: none; border-bottom: 1px solid var(--rule); } .navrow::-webkit-scrollbar { display: none; } .navrow a { flex: none; display: flex; align-items: center; min-width: 44px; padding: 8px 0 12px; color: var(--mute); font: 400 17px var(--font-display); white-space: nowrap; } .navrow a:hover { color: var(--paper-dim); background: var(--hover); } .navrow a.active { color: var(--patina); border-bottom: 2px solid var(--patina); margin-bottom: -1px; background: var(--patina-wash); padding-left: 6px; padding-right: 6px; margin-left: -6px; margin-right: -6px; } .admin-page { padding: 0 20px 40px; } .admin-page > .sec, .admin-sheet .readers h2, .admin-sheet .lanes h2 { position: relative; margin: 0; padding: 26px 0 8px; font: 500 11px/1 var(--font-mono); letter-spacing: .2em; text-transform: uppercase; color: var(--mute-2); } .admin-page > .sec::before, .admin-sheet .readers h2::before, .admin-sheet .lanes h2::before { content: ""; position: absolute; top: 14px; left: 0; width: 34px; height: 2px; background: var(--patina); } .admin-sheet .readers, .admin-sheet .lanes { margin: 0; padding: 0 0 16px; border-bottom: none; } .admin-sheet .readerlist { margin: 0; padding: 0; list-style: none; } .admin-sheet .readerlist li { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 16px; min-height: 48px; padding: 10px 0; border-top: 1px solid var(--rule); } .admin-sheet .reader-actions { display: flex; flex: 0 0 auto; gap: 28px; align-items: center; } .admin-sheet .reader-actions .ghost, .admin-sheet .c-act .ghost { font-size: 12px; color: var(--patina); } .admin-sheet .reader-actions .ghost.danger, .admin-sheet .c-act .ghost.danger { color: var(--danger); } .admin-sheet .readerlist form { margin: 0; } .admin-sheet .ghost.danger { color: var(--danger); } .admin-sheet .ghost.danger:hover { color: var(--danger); border-bottom-color: var(--danger); } .admin-sheet .reader-id { font: 500 15px/1.5 var(--font-mono); letter-spacing: .01em; color: var(--paper); } .admin-sheet .reader-sessions, .admin-sheet .reader-sightings, .admin-sheet .reader-blocked { font: 500 13px/1.4 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; } .admin-sheet .reader-sessions { color: var(--paper-dim); } .admin-sheet .reader-sightings { color: var(--mute); } .admin-sheet .reader-blocked { color: var(--patina); } /* A single grid keeps row rules continuous; cell padding supplies gutters. */ .admin-sheet .sechead { display: flex; align-items: baseline; justify-content: space-between; gap: 20px; flex-wrap: wrap; } .admin-sheet .sechead .statusline { padding: 0 0 8px; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; } .admin-sheet .statusline { margin: 0; padding: 0 0 10px; font: 500 12px/1 var(--font-mono); letter-spacing: .04em; color: var(--mute-2); } /* The verdict line is set in the data face, not the display face: it is three counts, not a page title. The judgement is the only bright thing. */ .admin-sheet .verdict { padding: 16px 0 12px; border-bottom: 1px solid var(--rule); font: 500 15px/1.6 var(--font-mono); letter-spacing: .04em; color: var(--mute); } .admin-sheet .verdict .attn { color: var(--patina); } .admin-sheet .verdict .counts b { color: var(--paper); font-weight: 500; } .admin-sheet .tbl { display: grid; grid-template-columns: minmax(240px, 1fr) 156px 92px 110px 76px minmax(150px, 220px) 140px; column-gap: 0; font-variant-numeric: tabular-nums; } .admin-sheet .tbl .thead { display: contents; } .admin-sheet .tbl .thead > * { padding: 10px 14px 8px 0; border-bottom: 1px solid var(--rule); font: 500 12px/1 var(--font-mono); letter-spacing: .12em; text-transform: uppercase; color: var(--mute-2); } .admin-sheet .tbl .trow { display: contents; } .admin-sheet .tbl .trow > * { padding: 11px 14px 11px 0; border-bottom: 1px solid var(--rule); font: 500 15px/1.5 var(--font-mono); letter-spacing: .01em; color: var(--paper-dim); } .admin-sheet .tbl .thead > *:last-child, .admin-sheet .tbl .trow > *:last-child { padding-right: 0; } .admin-sheet .tbl .c-title, .admin-sheet .tbl .c-site { font: 400 18px/1.35 var(--font-display); letter-spacing: 0; color: var(--paper); } .admin-sheet .tbl .c-title a:hover, .admin-sheet .tbl .c-act .ghost:hover { color: var(--patina); } .admin-sheet .tbl .trow.attention .c-title { color: var(--patina); } .admin-sheet .tbl .c-ch, .admin-sheet .tbl .c-rd { text-align: right; padding-right: 26px; } .admin-sheet .tbl .trow .c-note .mark { margin-right: 8px; } .admin-sheet .tbl .c-act { text-align: right; } .admin-sheet .tbl .c-act .ghost + .ghost { margin-left: 28px; } .admin-sheet .tbl .trow > .confirm-row { grid-column: 1 / -1; padding: 10px 12px; border-bottom: none; } .admin-sheet .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); margin: 4px 0 0; border-bottom: 1px solid var(--rule); } .admin-sheet .stat { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 11px 24px 11px 0; } .admin-sheet .stat + .stat { border-left: 1px solid var(--rule); padding-left: 24px; } .admin-sheet .stat .lbl { font: 500 13px/1.3 var(--font-mono); letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); } .admin-sheet .stat .fig { font: 500 19px/1 var(--font-mono); font-variant-numeric: tabular-nums; color: var(--paper); } .admin-sheet .stat .fig.zero { color: var(--mute); } .admin-sheet .stat a.fig { /* The hover rule below sets the border on :hover only, which shifts the row a pixel on hover; the transparent baseline holds the layout. */ border-bottom: 1px solid color-mix(in oklch, var(--patina) 22%, transparent); } .admin-sheet .stat a.fig:hover { color: var(--patina); border-bottom: 1px solid var(--patina); background: var(--patina-wash); } @media (max-width: 600px) { .admin-sheet .stat + .stat { border-left: none; border-top: 1px solid var(--rule); padding-left: 0; } } .admin-sheet .mark, .admin-sheet .mark-faint { font: 500 12px/1.2 var(--font-mono); letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; padding: 2px 6px; border-radius: 2px; border: 1px solid transparent; } .admin-sheet .mark { color: var(--patina); background: var(--patina-wash); border-color: var(--patina-line); } .admin-sheet .mark-faint { color: var(--mute-2); background: transparent; border-color: transparent; padding-left: 0; padding-right: 0; } .admin-sheet .mark.mark-strong { font-size: 12px; letter-spacing: .12em; color: var(--patina); background: var(--patina-wash); border-color: var(--patina-line); padding: 3px 7px; } .admin-sheet .mark.mark-strong::before { content: ""; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--patina); margin-right: 8px; vertical-align: .08em; } .admin-sheet .mark.bad { color: var(--slate); background: var(--slate-wash); border-color: var(--slate); font-weight: 600; } .admin-sheet .mark.bad::before { content: ""; display: inline-block; width: 7px; height: 7px; background: var(--slate); margin-right: 8px; vertical-align: .08em; border-radius: 1px; /* square, not circle — distinguishes from .mark-strong patina dot */ } .admin-sheet .tbl.sites { grid-template-columns: 170px repeat(4, 106px) minmax(180px, 1fr); } .admin-sheet .tbl.sites .c-site, .admin-sheet .tbl.lanes .c-site { font: 400 18px/1.35 var(--font-display); letter-spacing: 0; color: var(--paper); } .admin-sheet .tbl.sites .c-state { color: var(--mute-2); white-space: nowrap; padding: 2px 7px; border-radius: 2px; border: 1px solid transparent; background: transparent; justify-self: start; align-self: center; font: 500 11px/1 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; } .admin-sheet .tbl.sites .c-state.ok { color: var(--patina); background: var(--patina-wash); border-color: var(--patina-line); } .admin-sheet .tbl.sites .c-state.bad { color: var(--slate); background: var(--slate-wash); border-color: var(--slate); font-weight: 600; } .admin-sheet .tbl.lanes .c-skip .ok { color: var(--patina); background: var(--patina-wash); border: 1px solid var(--patina-line); padding: 1px 5px; border-radius: 2px; } .admin-sheet .tbl.lanes .trow.attention .c-site { color: var(--patina); } .admin-sheet .tbl.lanes .c-skip .bad { color: var(--slate); background: var(--slate-wash); border: 1px solid var(--slate); padding: 1px 5px; border-radius: 2px; font-weight: 600; } .admin-sheet .tbl.lanes .trow.attention { background: var(--patina-wash); box-shadow: inset 1px 0 0 var(--patina); } .admin-sheet .tbl.series .trow.attention { background: var(--patina-wash); box-shadow: inset 1px 0 0 var(--patina); } .admin-sheet .tbl.sites .thead > *:nth-child(n+2):nth-child(-n+5), .admin-sheet .tbl.sites .trow > *:nth-child(n+2):nth-child(-n+5) { padding-right: 0; text-align: center; } .admin-sheet .tbl.lanes { grid-template-columns: 150px 62px 88px 66px 118px minmax(0, 1fr) 168px; } .admin-sheet .tbl.lanes .c-skip { white-space: normal; } .admin-sheet .tbl.lanes .c-skip > * { white-space: nowrap; } .admin-sheet .tbl.lanes .c-ctrl { text-align: right; } .admin-sheet .tbl.lanes .thead > *:nth-child(n+2):nth-child(-n+4), .admin-sheet .tbl.lanes .trow > *:nth-child(n+2):nth-child(-n+4) { padding-right: 26px; text-align: right; } .admin-sheet .tbl.series { grid-template-columns: 150px 84px 104px 76px minmax(120px, 1fr) 212px; row-gap: 4px; } .admin-sheet .tbl.series .thead > *:first-child { padding-left: 20px; } .admin-sheet .tbl.series .thead > *:last-child { padding-right: 20px; } /* The site cell's colour is a class, never an inline style: site is client-supplied and unvalidated, and a hostile value reaching a CSS context would render ZgotmplZ, while an unknown class degrades to the unstyled cell. */ .admin-sheet .tbl .c-site.site-asura { color: var(--asura); } .admin-sheet .tbl .c-site.site-demonic { color: var(--demonic); } .admin-sheet .tbl .c-site.site-comix { color: var(--comix); } .admin-sheet .tbl .c-site.site-kagane { color: var(--kagane); } .admin-sheet .tbl .c-site.site-novelfull { color: var(--novelfull); } .admin-sheet .tbl .c-site.site-lightnovelworld { color: var(--lightnovelworld); } .admin-sheet .tbl.series .trow { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; padding: 13px 20px 14px; } .admin-sheet .tbl.series .trow.band { background: var(--hover); } .admin-sheet .tbl.series .trow > * { padding: 0 14px 0 0; border-bottom: none; } .admin-sheet .tbl.series .c-title { display: flex; grid-column: 1 / -1; align-items: baseline; gap: 16px; padding: 0 0 4px; } .admin-sheet .tbl.series .c-title .mark { margin-left: auto; } .admin-sheet .filterbar { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 22px; padding: 16px 0 14px; border-bottom: 1px solid var(--rule); } .admin-sheet .fsel { display: flex; align-items: baseline; gap: 10px; } .admin-sheet .fsel > span { font: 500 11px/1 var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--mute-2); } .admin-sheet .fsel select, .admin-sheet .pausebar select { color-scheme: dark; background: var(--ink); border: none; border-bottom: 1px solid var(--field-line); color: var(--paper); font: 400 16px/1.4 var(--font-display); padding: 4px 4px 5px 0; } .admin-sheet .fsel select:hover, .admin-sheet .pausebar select:hover { border-bottom-color: var(--patina); } .admin-sheet .fsel select option, .admin-sheet .pausebar select option { background: var(--ink); color: var(--paper); } .admin-sheet .segrow { display: inline-flex; gap: 2px; } .admin-sheet .segrow a { padding: 6px 11px 7px; border-bottom: 2px solid transparent; color: var(--mute); font: 500 14px/1 var(--font-mono); } .admin-sheet .segrow a.active { border-bottom-color: var(--patina); color: var(--patina); background: var(--patina-wash); } .admin-sheet .segrow a:hover { color: var(--paper-dim); background: var(--hover); } .admin-sheet .listhead { display: flex; align-items: baseline; gap: 12px; padding: 16px 0 4px; color: var(--paper); font: 400 18px/1.2 var(--font-display); } .admin-sheet .listhead .lbl { color: var(--mute); } .admin-sheet .listhead .lbl em { color: var(--patina); font-style: normal; } .admin-sheet .pager { display: flex; align-items: center; gap: 14px; padding: 14px 0 0; color: var(--mute-2); font: 500 11px/1 var(--font-mono); letter-spacing: .14em; text-transform: uppercase; } .admin-sheet .pager .pg { color: var(--paper-dim); } .admin-sheet .pager .pg:hover { color: var(--paper); } .admin-sheet .pager .pg.disabled { color: var(--mute); pointer-events: none; } .admin-sheet .empty { padding: 28px 0; text-align: center; } .admin-sheet .empty strong { color: var(--paper); font: 400 20px var(--font-display); } .admin-sheet .empty p { margin: 6px 0 0; font: 14px/1.5 var(--font-body); color: var(--mute); } .admin-sheet .confirm-row { display: flex; align-items: center; gap: 12px; padding: 10px 12px; background: var(--danger-wash); } .admin-sheet .confirm-row span { flex: 1 1 16ch; color: var(--danger-soft); font: 400 15px/1.3 var(--font-display); } .admin-sheet .confirm-row div { display: flex; flex: none; gap: 12px; margin-left: auto; } /* The finish confirm is the one calm row on the admin sheet: a reversible move wears the recessed ash, never the remove wash above, and its affirmative takes the sheet's patina accent (issue #158). */ .admin-sheet .confirm-row.calm { background: var(--ash); } .admin-sheet .confirm-row.calm span { color: var(--paper-dim); } .admin-sheet .confirm-row.calm .go { background: var(--patina); color: var(--ink); } .admin-sheet .readerlist li .confirm-row { width: 100%; margin-top: 8px; } /* Busy: same muted hairline the reader library uses, never ember. */ .admin-sheet .trow.htmx-request, .admin-sheet #readers.htmx-request, .admin-sheet #lanes.htmx-request, .admin-sheet #detail-meta.htmx-request, .admin-sheet .dform.htmx-request { opacity: .6; pointer-events: none; } .admin-sheet .ghost:focus-visible { outline: 2px solid var(--patina); outline-offset: 2px; color: var(--paper); } .admin-sheet .tbl.series .row-msg { grid-column: 1 / -1; margin-top: 6px; color: var(--danger-soft); font: 400 13px/1.4 var(--font-body); } .admin-sheet .detail-back { margin: 18px 0 0; } .admin-sheet .detail-title { margin: 10px 0 2px; color: var(--paper); font: 400 28px/1.25 var(--font-display); } .admin-sheet .detail-key { margin: 0; color: var(--mute-2); font: 500 11px/1.4 var(--font-mono); letter-spacing: .08em; } .admin-sheet .detail-meta { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 10px 0 0; color: var(--mute-2); font: 500 12px/1.5 var(--font-mono); letter-spacing: .08em; text-transform: uppercase; } .admin-sheet .cover { width: 160px; aspect-ratio: 3 / 4; display: flex; align-items: center; justify-content: center; margin: 18px 0 4px; background: var(--hatch); color: var(--mute-2); font: 500 10px/1 var(--font-mono); letter-spacing: .2em; text-transform: uppercase; } .admin-sheet .detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 28px; } .admin-sheet .dform { padding: 14px 0 0; } .admin-sheet .dform h3 { margin: 0 0 4px; color: var(--mute-2); font: 500 10px/1 var(--font-mono); letter-spacing: .2em; text-transform: uppercase; } .admin-sheet .dform .field { display: flex; gap: 10px; margin-top: 8px; } .admin-sheet .dform input { min-width: 0; padding: 8px 10px; border: 1px solid var(--field-line); background: var(--ink); color: var(--paper); font: 500 14px var(--font-mono); outline: none; } /* Focus follows the chapter form's idiom — paper, not heat: a red border on a valid number field reads as "invalid". */ .admin-sheet .dform input:focus { border-color: var(--paper); } .admin-sheet .dform .hint { margin: 0; color: var(--mute-2); font: 500 12px/1.4 var(--font-mono); letter-spacing: .04em; } .admin-sheet .pausebar { display: flex; align-items: center; justify-content: flex-end; gap: 8px; } @media (max-width: 719px) { .admin-sheet .topbar { flex-wrap: wrap; row-gap: 12px; } .admin-sheet .brand { flex: 1 1 100%; } .admin-sheet .topbar-actions { margin-left: auto; } } @media (max-width: 1019px) { .admin-sheet .tbl.lanes { display: block; } .admin-sheet .tbl.lanes .thead { display: none; } .admin-sheet .tbl.lanes .trow { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; padding: 12px 0; border-bottom: 1px solid var(--rule); } .admin-sheet .tbl.lanes .trow > * { padding: 0; border-bottom: none; text-align: left; } .admin-sheet .tbl.lanes .c-site { width: 100%; padding-bottom: 2px; } .admin-sheet .tbl.lanes .c-ctrl { margin-left: auto; text-align: right; } /* The thead is gone, so every cell that was only legible under a column heading prints its own. The label is data, not decoration: without it "12 40 6h" is three numbers in the same face with no way to tell which is which. Set in the mono face at label size so the figure still leads visually. */ .admin-sheet .tbl.lanes [data-label] { white-space: nowrap; } .admin-sheet .tbl.lanes [data-label]::before { content: attr(data-label) " "; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); } } @media (max-width: 899px) { .admin-sheet .tbl, .admin-sheet .tbl.lanes { display: block; } .admin-sheet .tbl .thead { display: none; } .admin-sheet .tbl .trow { display: flex; flex-wrap: wrap; /* Cells no longer share a font size — a label-value pair is 11px over 15px — so they hang off the baseline, not off the box top. */ align-items: baseline; /* Wider than the mouse layout's column gutter: each cell now carries its own label, so the eye needs the pair to read as one unit. */ gap: 2px 18px; padding: 11px 0; border-bottom: 1px solid var(--rule); } .admin-sheet .tbl .trow > * { padding: 0; border-bottom: none; text-align: left; } .admin-sheet .tbl .c-ch, .admin-sheet .tbl .c-rd { padding-right: 0; text-align: left; } .admin-sheet .tbl .c-title { width: 100%; padding-bottom: 2px; } .admin-sheet .tbl.series .trow { display: flex; } .admin-sheet .tbl.series .trow > * { padding: 0; } .admin-sheet .detail-grid { grid-template-columns: 1fr; } .admin-sheet .tbl .c-act { margin-left: auto; } /* A cell is one label-value pair and must not break between them. */ .admin-sheet .tbl [data-label] { white-space: nowrap; } .admin-sheet .tbl [data-label]::before { content: attr(data-label) " "; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--mute-2); } } /* ---- touch: the reader's library is sized for a thumb throughout, this sheet was not — it is a data surface that grew on a mouse. Gated on the input method rather than the width, because the phone is not the only coarse pointer and a 1400px touch laptop has the same problem. Vertical only: horizontal space is what this surface is short of. ---- */ @media (pointer: coarse) { /* The figure is the door — the label beside it is not a link — so the row gives up its own padding to the cell that has to be hit. */ .admin-sheet .stat { padding: 0 24px 0 0; } /* A one-digit count is 11px wide: the minimum is on the box, not the glyph, or the door is a target the thumb has to aim at. */ .admin-sheet .stat .fig { min-width: 44px; padding: 14px 0 14px 20px; text-align: right; } .admin-sheet .tbl .c-title a, .admin-sheet .tbl a.fig, .admin-sheet .tbl a.c-site, /* The collapsed lanes row sets padding on every direct cell, which is a more specific selector than the line above — its own Site link has to be named here or it keeps the mouse metric. */ .admin-sheet .tbl.lanes .trow > a.c-site { display: inline-block; min-width: 44px; padding: 10px 0; } .admin-sheet .pager { gap: 20px; } .admin-sheet .pager .pg { padding: 16px 0; } .admin-sheet .segrow a { padding: 15px 11px 16px; } .admin-sheet .fsel select, .admin-sheet .pausebar select, .admin-sheet .dform input { padding-top: 11px; padding-bottom: 12px; } }