af07314bb6
Uncommitted work from three design runs on this branch, against one design system: docs/design-system.md is updated to match the CSS, not the reverse. Library (Reader-facing): - .chrome sticks at top: 0. Search and the tab row were unreachable three screens into a 300-item library, which is exactly where they earn their keep; everything above them still scrolls away on purpose. - One :focus-visible ring (2px --paper) on the nine controls that defined none and fell back to the UA blue. .searchbar keeps its border recolour as a resting cue but no longer stands in for a ring. - Mono labels lift 10px -> 11px everywhere. The brief names night reading and glare as the usage scene; 10px small-caps was where taste overrode it. - A card in flight past 2s says "Saving..." and carries aria-busy. htmx sets neither, so the wait up to its 15s timeout was silent in both channels. - Titles clamp at 3 lines; .is-new .title takes width: fit-content, or -webkit-box stretches the ember underline past the text it sizes to. /admin: - Overview routes into Lanes when a lane is unhealthy, prefixes each figure with its column word on the phone layout that drops the thead, labels state cells for a screen reader, and has an empty state where the sites table assumed rows. - The admin shell picks up the library's chrome: htmx 15s timeout, the shared #notice slot, #sr-announce, filter.js. admin.css follows the same pass. - admin_render_test.go and card_render_test.go render the templates directly, so markup regressions in either surface fail without a browser. Login: - DISCORD_GUILD_NAME (optional) names the community on the login screen and in the refusal message, so a stranger knows which Discord to ask for an invite. Unset degrades to a generic label; neither form names the guild id. Handlers: - maxChapterNum (9999) bounds both typed-chapter paths. uiChapter and adminSeriesCorrectLatest each parsed a float64 with no ceiling, so a hand-rolled POST stored 1e308 and every later reader of that row inherited it. Matches the max on the card's chapter input. go test ./... green.
1045 lines
21 KiB
CSS
1045 lines
21 KiB
CSS
: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;
|
|
}
|
|
}
|