pdf-quiz-generator/frontend/src/pages/ArticlesPage.css
Daniel e62a742b73
Some checks failed
Tests / backend (push) Failing after 5s
Tests / frontend (push) Successful in 27s
Tests / e2e (push) Failing after 28s
fix: Reading cannot edit, and the menu button reaches the menu everywhere
Reading is read-only. Edit, Unpublish and Generate cards sat in the
reading toolbar, so an educator reading between questions was one
mis-tap from the editor — which is how somebody opened edit mode on
Bronchiolitis in the middle of a session. All three live at
/editorial/articles/:id now, with one named door through from Reading
for whoever may edit. An ?edit=1 on a reading address is not ignored:
the ask is fine, the address is wrong, so it is taken to the editorial
one.

And a cross-reference followed from Editorial stays in Editorial.
Reading and Editorial are two modes of the same page, and one link out
of the second into the first put an educator into the learner's view of
the next article with no way back to the queue.

The menu button now reaches the menu on every page it has been taken
over on. On a phone the burger belongs to whatever is on screen; the
player handed back the site menu as a tab inside its drawer, and an
article and an answer review did not — so on those two the only menu
button on the page could not open the menu. Both have the player's own
two tabs now, from one list of links rather than three copies of it
(one of which pointed Qbank at /questions, which has never been a
route).

And the button shows a cross while what it opens is open. It kept its
three bars behind an open drawer, an inch from the cross inside that
drawer: two controls disagreeing about what was on screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-13 04:36:44 +02:00

547 lines
31 KiB
CSS

.articles-page, .article-page { max-width: 1080px; margin: 0 auto; padding: 4px 0 24px; }
.articles-header { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; }
.articles-header h1, .article-header h1 { margin: 0 0 4px; font-size: 1.5rem; }
.articles-subtitle { margin: 0; color: var(--text-muted); font-size: 0.88rem; }
.articles-toolbar { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.articles-toolbar .input { max-width: 320px; }
.articles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; }
.article-card { background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--card-radius); padding: 16px; text-decoration: none; color: var(--text); transition: box-shadow .15s ease, transform .15s ease; }
.article-card:hover { box-shadow: 0 8px 24px rgba(2, 70, 145, .12); transform: translateY(-2px); }
.article-card h3 { margin: 0 0 6px; font-size: 1.02rem; }
.article-card p { margin: 0 0 8px; color: var(--text-muted); font-size: .85rem; }
.article-card-meta { color: var(--text-muted); font-size: .75rem; }
.article-status-draft { font-size: .65rem; font-weight: 700; text-transform: uppercase; color: #92400e; background: #fef3c7; padding: 2px 7px; border-radius: 10px; margin-left: 6px; vertical-align: middle; }
.breadcrumbs { font-size: .8rem; color: var(--text-muted); margin-bottom: 8px; }
.breadcrumbs a { color: var(--primary); text-decoration: none; }
.article-header { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
.article-header-actions { display: flex; gap: 8px; }
/* ── The reading layout ───────────────────────────────────────────────
Rail flush to the left edge of the window, prose taking everything to the
right of it. `--article-top` is the measured height of the site header, set
by the reader: the navbar's strip collapses as you scroll, and a number
written down here would go stale the moment that changes. */
.app-main.is-bleed { width: 100%; padding: 0; }
/* The navbar keeps 32px of air beneath it for pages that are cards on a
background. A reading page is not one — the rail and the article run to the
window edges, and a band of page showing above them reads as a gap where
something failed to load. */
.article-page.is-reading { max-width: none; margin: -32px 0 0; padding: 0; }
.article-notices { max-width: 1080px; margin: 0 auto; padding: 12px 28px 0; }
.article-layout {
--article-top: 98px;
display: grid;
grid-template-columns: 272px minmax(0, 1fr);
align-items: start;
}
/* A rail, not a card: the contents of a long article should stay in view for
its whole length, scrolling on their own when there are more sections than
screen. It keeps its own scroll so the page behind it never moves. */
.article-sections {
position: sticky;
top: var(--article-top);
height: calc(100dvh - var(--article-top));
/* The collapse handle hangs over the boundary, so nothing here may clip. */
overflow: visible;
background: var(--bg);
border-right: 1px solid var(--border);
}
.article-rail-scroll { height: 100%; overflow-y: auto; padding: 16px 12px 28px 20px; }
.article-sections h4 { margin: 0 0 10px; font-size: .82rem; font-weight: 700; line-height: 1.35; color: var(--text); }
/* Collapsing it hands the width to the prose, which is what a long table or a
wide image needs and what nothing else on the page can give it. Remembered
between articles, because it is a statement about how you read. */
.article-layout.is-railed-off { grid-template-columns: 30px minmax(0, 1fr); }
.article-layout.is-railed-off .article-sections { display: none; }
.article-rail-hide, .article-rail-reopen {
display: flex; align-items: center; justify-content: center;
width: 26px; height: 26px; padding: 0;
background: var(--card-bg); border: 1px solid var(--border); border-radius: 50%;
color: var(--text-muted); font: inherit; font-size: 1rem; line-height: 1; cursor: pointer;
}
.article-rail-hide:hover, .article-rail-reopen:hover { border-color: var(--primary); color: var(--primary); }
/* On the boundary between the rail and the prose — where the reader is looking
when they decide they want the width. */
.article-rail-hide { position: absolute; top: 16px; right: -13px; z-index: 3; }
/* And where the rail was, so putting it back is where you last saw it. */
.article-rail-reopen { position: sticky; top: calc(var(--article-top) + 16px); margin: 16px 0 0 2px; }
.article-sections ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.section-link { width: 100%; text-align: left; background: none; border: none; border-radius: 6px; padding: 7px 10px; font-size: .84rem; line-height: 1.4; color: var(--text); cursor: pointer; }
.section-link:hover { background: var(--hover, #eef4fb); }
.section-link.active { background: var(--primary-soft, #dcebfa); color: var(--primary); font-weight: 600; }
.article-content {
min-width: 0;
background: var(--card-bg);
padding: 14px clamp(20px, 3.5vw, 56px) 48px;
min-height: calc(100dvh - var(--article-top));
}
.article-content h2 { margin-top: 0; }
.article-content img { max-width: 100%; border-radius: 8px; }
.article-content table { border-collapse: collapse; width: 100%; margin: 12px 0; }
.article-content td, .article-content th { border: 1px solid var(--border); padding: 6px 10px; }
/* Three steps, because a reader wants bigger or smaller and nothing finer. */
.article-text-s { font-size: .92rem; }
.article-text-m { font-size: 1rem; }
.article-text-l { font-size: 1.12rem; }
/* The icon toolbar sits above the title, opposite the trail back to the
library — the two things you do with an article that are not reading it. */
.article-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding-top: 10px; }
.article-trail { min-width: 0; }
.article-trail .breadcrumbs { margin-bottom: 0; }
.article-tools { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.article-tool {
display: inline-flex; align-items: baseline; justify-content: center; gap: 1px;
min-width: 32px; height: 32px; padding: 0 8px;
background: none; border: 1px solid var(--border); border-radius: 8px;
font: inherit; font-size: .95rem; line-height: 1; color: var(--text-muted); cursor: pointer;
}
.article-tool:hover { border-color: var(--primary); color: var(--primary); }
.article-tool.is-on { border-color: var(--primary); color: var(--primary); background: var(--primary-soft, #dcebfa); }
.article-tool-a { font-size: .72rem; }
.article-title { margin: 6px 0 0; font-size: 1.75rem; line-height: 1.2; }
.article-updated { font-size: .76rem; color: var(--text-subtle); margin: 8px 0 0; }
.article-summary { font-size: .95em; color: var(--text-muted); border-left: 3px solid var(--primary); padding-left: 12px; margin: 14px 0 4px; }
.article-summary p:last-child { margin-bottom: 0; }
/* Contents on a narrow screen, where there is no room for a rail beside prose. */
.article-drawer-toggle {
display: none; align-items: center; gap: 6px; height: 32px; padding: 0 12px;
background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px;
font: inherit; font-size: .82rem; color: var(--text); cursor: pointer;
}
.article-linked { margin-top: 22px; border-top: 1px solid var(--border); padding-top: 14px; }
.article-linked h3 { margin: 0 0 10px; font-size: 1rem; }
.linked-card { display: flex; justify-content: space-between; align-items: center; gap: 10px; border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin-bottom: 8px; flex-wrap: wrap; }
.article-edit { display: flex; flex-direction: column; gap: 8px; }
.article-edit .form-label { margin-top: 6px; }
@media (prefers-reduced-motion: reduce) {
.article-sections { transition: none !important; }
}
@media (max-width: 820px) {
/* No room for a rail beside prose. The contents become a panel over the
page, opened from the article's own bar — the site menu stays on the
navbar's button, so the two never compete for the same thumb. */
.article-layout, .article-layout.is-railed-off { grid-template-columns: minmax(0, 1fr); }
.article-sections {
position: fixed;
top: var(--article-top); bottom: 0; left: 0;
/* Above the split pane and above an open figure: the menu is pressed from
the header, which is on top of both, so a drawer that opened behind them
looked like a button that did nothing. */
z-index: 1250;
width: min(320px, 86vw);
height: auto;
transform: translateX(-101%);
/* Hidden rather than merely off-screen: a panel parked past the left edge
is still in the tab order, and tabbing into contents you cannot see is
how a keyboard reader loses the page. */
visibility: hidden;
border-right: 1px solid var(--border);
box-shadow: 8px 0 28px rgba(15, 23, 42, .18);
transition: transform .18s ease, visibility .18s;
}
.article-sections.open { transform: none; visibility: visible; }
/* The drawer's own head: the way out, and the two things behind this
button. The same shape the session player's drawer has, because it is
the same button — on a phone the burger belongs to whatever is on
screen, and the site menu is a tab inside it. Reading had only the
contents, so the menu button on an article could not reach the menu. */
.article-drawer-head {
display: flex; align-items: center; gap: 10px;
padding: 10px 12px; border-bottom: 1px solid var(--border); flex: none;
}
.article-drawer-close {
flex: none; width: 34px; height: 34px;
background: none; border: 0; border-radius: 8px;
font-size: 1rem; cursor: pointer; color: var(--text-muted);
}
.article-drawer-tabs {
display: flex; flex: 1; border: 1px solid var(--border);
border-radius: 8px; overflow: hidden;
}
.article-drawer-tabs button {
flex: 1; padding: 9px 6px; min-height: 40px;
font: inherit; font-size: .84rem; font-weight: 600;
background: var(--card-bg); border: 0; cursor: pointer; color: var(--text-muted);
}
.article-drawer-tabs button[aria-selected="true"] {
background: var(--option-sel-bg); color: var(--primary);
}
.article-drawer-menu { display: flex; flex-direction: column; padding: 6px; overflow-y: auto; }
.article-drawer-menu a {
padding: 12px 10px; min-height: 44px;
font-size: .9rem; text-decoration: none; color: var(--text); border-radius: 8px;
}
.article-drawer-menu a:hover { background: var(--bg); }
/* The dark ground behind an open drawer. Tapping it closes the drawer. */
.article-drawer-backdrop {
/* Below the header, not over it. Covering the header put a transparent
sheet across the very button that opened the drawer, so pressing it
again hit the backdrop instead — the drawer closed, but the button
looked dead. */
position: fixed; top: var(--article-top); left: 0; right: 0; bottom: 0;
z-index: 1249; background: rgba(15, 23, 42, .38);
}
.article-rail-hide, .article-rail-reopen { display: none; }
/* No Contents button. The menu in the header opens the contents on a phone,
and it is the control that is in the same place on every page — a second
one in the prose is the same door twice, which is what the quiz player had
until it did not. */
.article-drawer-toggle { display: none; }
.article-content { padding: 12px 16px 40px; min-height: 0; }
.article-title { font-size: 1.4rem; }
}
.comment-section { margin-top: 22px; border-top: 1px solid var(--border); padding-top: 14px; }
.comment-heading { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.comment-heading h3 { margin: 0; font-size: 1rem; }
.comment-subtitle { font-size: .74rem; color: var(--text-muted); }
.comment-compose { background: var(--input-bg); border: 1px solid var(--border); border-radius: 10px; padding: 10px; margin-bottom: 14px; }
.comment-input { width: 100%; border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; font-size: .88rem; resize: vertical; }
.comment-compose-footer { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.comment-count { margin-left: auto; font-size: .72rem; color: var(--text-muted); }
.comment-empty { color: var(--text-muted); font-size: .84rem; margin: 6px 0 0; }
.comment-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.comment { display: flex; gap: 10px; padding: 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--card-bg); }
.comment-avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--primary-soft, #dcebfa); color: var(--primary); font-weight: 700; display: flex; align-items: center; justify-content: center; font-size: .9rem; flex-shrink: 0; }
.comment-body { flex: 1; min-width: 0; }
.comment-meta { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; font-size: .84rem; }
.comment-meta span { color: var(--text-muted); font-size: .74rem; }
.comment-badge { font-style: normal; font-size: .66rem; font-weight: 700; text-transform: uppercase; color: #92400e; background: #fef3c7; padding: 1px 8px; border-radius: 10px; }
.comment-content { font-size: .88rem; }
.comment-content p { margin: 0 0 4px; }
.comment-actions { display: flex; gap: 6px; margin-top: 8px; }
.comment-load-more { margin-top: 10px; }
.comment-section { margin-top: 22px; border-top: 1px solid var(--border); padding-top: 14px; }
.comment-heading { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.comment-heading h3 { margin: 0; font-size: 1rem; }
.comment-subtitle { font-size: .74rem; color: var(--text-muted); }
.comment-compose { background: var(--input-bg); border: 1px solid var(--border); border-radius: 10px; padding: 10px; margin-bottom: 14px; }
.comment-input { width: 100%; border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; font-size: .88rem; resize: vertical; }
.comment-compose-footer { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.comment-count { margin-left: auto; font-size: .72rem; color: var(--text-muted); }
.comment-empty { color: var(--text-muted); font-size: .84rem; margin: 6px 0 0; }
.comment-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.comment { display: flex; gap: 10px; padding: 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--card-bg); }
.comment-avatar { width: 30px; height: 30px; border-radius: 50%; background: var(--primary-soft, #dcebfa); color: var(--primary); font-weight: 700; display: flex; align-items: center; justify-content: center; font-size: .9rem; flex-shrink: 0; }
.comment-body { flex: 1; min-width: 0; }
.comment-meta { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; font-size: .84rem; }
.comment-meta span { color: var(--text-muted); font-size: .74rem; }
.comment-badge { font-style: normal; font-size: .66rem; font-weight: 700; text-transform: uppercase; color: #92400e; background: #fef3c7; padding: 1px 8px; border-radius: 10px; }
.comment-content { font-size: .88rem; }
.comment-content p { margin: 0 0 4px; }
.comment-actions { display: flex; gap: 6px; margin-top: 8px; }
.comment-load-more { margin-top: 10px; }
@keyframes article-slide-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
/* ── Practise this topic ──────────────────────────────────────────── */
.article-practise { margin-top: 22px; border-top: 1px solid var(--border); padding-top: 16px; }
.article-practise-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.article-practise-head h3 { margin: 0; font-size: 1rem; }
.article-practise-count { font-size: .78rem; color: var(--text-muted); }
.article-practise-empty { color: var(--text-muted); font-size: .86rem; margin: 0; }
.article-practise-controls { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
.article-practise-controls label { display: flex; flex-direction: column; gap: 4px; }
.article-practise-controls label span { font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-subtle); }
.article-practise-controls select {
padding: 8px 11px; border: 1px solid var(--border); border-radius: 8px;
background: var(--input-bg); color: var(--text); font-size: .88rem;
}
.article-practise-error { color: var(--wrong-fg); font-size: .84rem; margin: 8px 0 0; }
.article-practise-manage { margin-top: 14px; }
.article-practise-manage > button {
background: none; border: none; padding: 0; cursor: pointer; font: inherit;
font-size: .78rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted);
}
.article-practise-manage > button:hover { color: var(--primary); }
.article-practise-manage ul { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.article-practise-manage li {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
border: 1px solid var(--border); border-radius: 8px; padding: 9px 11px; font-size: .85rem;
}
.article-practise-manage li span { flex: 1; min-width: 140px; overflow-wrap: anywhere; }
.article-practise-manage li em { font-size: .72rem; color: var(--text-muted); font-style: normal; }
@media (max-width: 640px) {
.article-practise-controls { align-items: stretch; }
.article-practise-controls label { flex: 1; }
.article-practise-controls .btn { width: 100%; }
}
/* Category browser on the articles page */
.articles-browse {
display: inline-flex; align-items: center; gap: 8px; min-height: 42px;
padding: 9px 14px; background: var(--card-bg); border: 1px solid var(--border);
border-radius: 8px; font: inherit; font-size: .88rem; color: var(--text); cursor: pointer;
}
.articles-browse:hover { border-color: var(--primary); color: var(--primary); }
.articles-browse[aria-expanded='true'] { border-color: var(--primary); color: var(--primary); }
.articles-browser { margin-bottom: 16px; }
/* ── Collapsible sections ─────────────────────────────────────────────
An article is a reference you consult, so it opens as a contents page:
headings only, each expanding where it sits. */
/* The row above the sections. It parks under the site header as you read, so
the depth switch and the expand control stay reachable and the trail can say
where in the article you have got to once the title has scrolled away. */
.asec-controls {
position: sticky;
top: var(--article-top);
z-index: 12;
display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
margin: 16px 0 0;
padding: 8px 0;
background: var(--card-bg);
border-bottom: 1px solid var(--border);
}
.asec-controls-left, .asec-controls-right { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* Where you are, once the heading has gone: article, then the section under
the reader's eye — the same one the rail marks. */
.asec-where { min-width: 0; font-size: .8rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.asec-where-section { color: var(--text); font-weight: 600; }
.asec-list { border-top: 1px solid var(--border); }
.asec { scroll-margin-top: calc(var(--article-top, 98px) + 56px); border-bottom: 1px solid var(--border); }
.asec-heading { margin: 0; font-size: 1rem; }
.asec-head {
display: flex; align-items: center; gap: 12px; width: 100%;
min-height: 52px; padding: 13px 4px 13px 8px;
background: none; border: 0; border-radius: 8px;
font: inherit; font-size: 1rem; font-weight: 700; color: var(--text);
text-align: left; cursor: pointer;
}
.asec-head:hover { background: var(--bg); }
.asec-head:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.asec-title { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.asec-chevron {
flex-shrink: 0; font-size: 1.1rem; line-height: 1; color: var(--text-muted);
transition: transform 0.16s ease;
}
.asec-head[aria-expanded='true'] .asec-chevron { transform: rotate(180deg); }
/* `em`, not `rem`: the text-size control sets a size on the article and the
prose inside it has to follow. */
.asec-body { padding: 0 8px 18px; font-size: 0.95em; line-height: 1.65; }
.asec-body > :first-child { margin-top: 0; }
/* A sub-section is drawn inside its parent, indented and quieter, so the
hierarchy is visible without a second heading level shouting. */
.asec-depth-1 { margin: 10px 0 0 4px; border-left: 2px solid var(--border); border-bottom: 0; padding-left: 10px; }
.asec-depth-1 .asec-head { min-height: 44px; font-size: 0.92rem; font-weight: 600; padding: 10px 4px 10px 6px; }
.asec-depth-1 .asec-body { padding-bottom: 10px; }
.asec.is-target > .asec-heading > .asec-head { box-shadow: inset 3px 0 0 var(--primary); color: var(--primary); }
.asec.is-references .asec-body { font-size: 0.86rem; color: var(--text-muted); }
/* Contents list, with sub-sections under the section they belong to. */
.atoc-sub { margin: 2px 0 4px 10px !important; padding-left: 8px !important; border-left: 1px solid var(--border); }
.atoc-sub .section-link { font-size: 0.8rem; color: var(--text-muted); }
.atoc-sub .section-link.active { color: var(--primary); }
@media (max-width: 640px) {
.asec-head { font-size: 0.95rem; }
.asec-body { font-size: 0.92rem; }
}
/* One topic, three readings — at the right of the row over the sections. The
rail is filtered by the same choice, so the contents can never list a
section the body is no longer showing. */
.aview-group { display: inline-flex; align-items: center; gap: 10px; }
/* Summary is a toggle, not a tab: it is the whole topic or the part of it
worth revising, and it names its own state so a reader can tell at a glance
why two thirds of the contents are not there. */
.aview-only {
display: inline-flex; align-items: center; gap: 8px;
min-height: 36px; padding: 7px 14px; cursor: pointer;
font: inherit; font-size: 0.83rem; font-weight: 600;
color: var(--text-muted); background: var(--card-bg);
border: 1px solid var(--border); border-radius: 8px;
}
.aview-only span { font-size: 0.95rem; line-height: 1; }
.aview-only:hover { color: var(--text); border-color: var(--text-subtle); }
.aview-only.is-on {
color: var(--primary); border-color: var(--primary);
background: color-mix(in srgb, var(--primary) 8%, transparent);
}
/* Six pixels, not two. At two the selected tab's fill ran straight into the
one beside it and the pair read as one wide control with a coloured half. */
.aview-switch { display: inline-flex; gap: 6px; padding: 3px; background: var(--bg); border: 1px solid var(--border); border-radius: 9px; }
.aview {
min-height: 34px; padding: 6px 13px; border: 0; border-radius: 7px; cursor: pointer;
background: none; font: inherit; font-size: 0.83rem; font-weight: 600; color: var(--text-muted);
}
.aview:hover { color: var(--text); }
/* Loud on purpose: this is the control that decides how much of the topic is
on screen, and a reader who cannot see which way it is set cannot explain
why two thirds of the contents just went away. */
.aview.is-active {
background: var(--primary); color: #fff;
box-shadow: 0 1px 3px rgba(15, 23, 42, .22);
}
.aview.is-active:hover { color: #fff; }
/* Sources for the whole article, not markers scattered through the prose. */
.article-references { margin-top: 26px; padding-top: 14px; border-top: 1px solid var(--border); }
.article-references h2 { margin: 0 0 10px; font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-subtle); }
.article-references ol { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 5px; }
/* A source title is often a filename with underscores in it and no spaces to
break at — `2021_Fleisher_&_Ludwig's_Textbook_of_Pediatric_Emergency…` — so
the line has to be allowed to break anywhere or it runs off the screen. */
.article-references li {
font-size: 0.83rem; line-height: 1.5; color: var(--text-muted);
overflow-wrap: anywhere; word-break: break-word;
}
.article-ref-title { color: var(--text); font-weight: 600; }
.article-ref-pages { font-variant-numeric: tabular-nums; }
/* The checkable part of a paper. Set apart from the citation so the eye finds
it — plain text, because a reference list is what was read, not a set of
doors out of the article. */
.article-ref-pmid {
margin-left: 8px; font-size: .78rem; font-weight: 600;
color: var(--text-muted); white-space: nowrap;
}
@media (max-width: 900px) {
/* Four things in a bar this narrow is three too many; the rail and the
heading already say where you are. */
.asec-where { display: none; }
}
@media (max-width: 640px) {
.asec-controls { gap: 8px; }
.aview { padding: 6px 10px; font-size: 0.79rem; }
}
/* A view that is one section is shown as prose: no heading repeating the tab
above it, no chevron hiding the only thing there is to read. */
.asec-sole { padding-top: 4px; }
/* Coming from Editorial to write one. The queue is where that person was
working; this page is a library, and without a way back the only route is
the browser's own button. */
.articles-back {
display: inline-block; margin-bottom: 6px;
font-size: 0.82rem; font-weight: 600; color: var(--text-muted); text-decoration: none;
}
.articles-back:hover { color: var(--primary); }
.articles-form-actions { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
/* A link, not a third button: leaving is not the same weight as the two
decisions about the form in front of it. */
.articles-leave {
margin-left: auto; font-size: 0.82rem; font-weight: 600;
color: var(--text-muted); text-decoration: none;
}
.articles-leave:hover { color: var(--primary); text-decoration: underline; }
@media (max-width: 560px) {
/* Three controls on one line becomes two lines rather than three squeezed
ones, and the way out stops being pushed off the right edge. */
.articles-form-actions { flex-wrap: wrap; }
.articles-leave { margin-left: 0; width: 100%; }
}
/* Leaving the editor. Save was the only control in the header, so the way out
of an editing sitting was the browser's back button — which throws the work
away and does not say so. */
.article-back {
display: inline-block; margin-bottom: 4px; padding: 0;
border: 0; background: none; cursor: pointer;
font: inherit; font-size: 0.82rem; font-weight: 600; color: var(--text-muted);
}
.article-back:hover { color: var(--primary); }
.article-discard-confirm {
display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap;
font-size: 0.82rem; color: var(--text-muted);
}
/* Deleting. At the foot of the editor and quiet until it is asked for: the
consequence differs by whether the article was ever published, so the
confirmation says which one this is rather than asking "are you sure". */
.article-danger { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); }
.article-danger p { margin: 0 0 10px; font-size: 0.86rem; line-height: 1.55; color: var(--text-muted); max-width: 62ch; }
.article-danger-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.article-danger-open { color: var(--wrong-fg); }
/* The identifiers, at the top of the editor. Both are looked for and neither
was anywhere on the page: the id had to be read out of the address bar and
the slug out of the form field below.
The third chip is the useful one — the cross-reference marker itself, id and
title already assembled, because that is what somebody wants these two
numbers *for*. */
.article-ids { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.article-id {
display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
padding: 4px 10px; border: 1px solid var(--border); border-radius: 999px;
background: var(--card-bg); font: inherit; font-size: 0.78rem; color: var(--text-muted);
}
.article-id:hover { border-color: var(--primary); color: var(--text); }
.article-id span { font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; font-size: 0.66rem; }
.article-id code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.76rem; color: var(--text);
}
.article-id.is-marker code { color: var(--primary); }
.article-id.is-marker { max-width: 100%; overflow: hidden; }
.article-id.is-marker code { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.article-id.is-copied { border-color: var(--primary); background: color-mix(in srgb, var(--primary) 8%, transparent); }
.article-id-said { font-size: 0.76rem; font-weight: 600; color: var(--primary); }
@media (max-width: 560px) {
.article-id.is-marker { display: none; } /* The marker is a desktop errand. */
}
/* Cross-references that point at nothing, said on save — the last moment the
person who wrote the link is still looking at it. The backend has computed
this since markers existed; nothing had ever shown it. */
.article-broken {
display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px;
padding: 11px 14px; border-radius: 9px;
background: color-mix(in srgb, var(--wrong-fg) 7%, transparent);
border: 1px solid color-mix(in srgb, var(--wrong-fg) 24%, transparent);
font-size: 0.86rem;
}
.article-broken code {
display: inline-block; margin-right: 6px; padding: 1px 6px; border-radius: 5px;
background: var(--card-bg); font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.78rem;
}
.article-broken-hint { font-size: 0.8rem; color: var(--text-muted); }
/* A job's progress, above the article it is working on. */
.article-ai-note {
display: flex; align-items: center; gap: 10px;
margin-bottom: 12px; padding: 10px 12px;
font-size: .86rem; color: var(--text);
background: var(--card-bg); border: 1px solid var(--border);
border-left: 3px solid var(--primary); border-radius: 8px;
}
.article-ai-note .spinner { width: 14px; height: 14px; border-width: 2px; flex: none; }
/* What a draft may draw on. Checkboxes with the reason beside them, because
"PubMed" alone does not tell an educator that the query leaves the building. */
.ai-draw-on { margin: 12px 0 4px; padding: 0; border: 0; }
.ai-draw-on legend {
padding: 0; font-size: .78rem; font-weight: 700; letter-spacing: .04em;
text-transform: uppercase; color: var(--text-muted);
}
.ai-draw-on label {
display: flex; align-items: flex-start; gap: 9px;
margin-top: 8px; cursor: pointer; font-size: .9rem;
}
.ai-draw-on input { margin-top: 3px; flex: none; }
.ai-draw-on span { display: flex; flex-direction: column; gap: 2px; }
.ai-draw-on small { font-size: .78rem; line-height: 1.5; color: var(--text-muted); }