/* Exam engine UI -- layered on Bootstrap 5.3 and css/spu.css. */

/* Room for the fixed top bar (+ progress strip) and bottom action bar. */
body { padding-top: 64px; padding-bottom: 84px; }

/* ---- Top bar ------------------------------------------------------------- */
/* The bar is a single flex row of cells -- home icon, brand, status cells,
   due pill, reference and log-out icons -- divided by hairlines rather than
   dashes.  The progress strip sits exactly on the bar's 4px bottom border, so
   an unfilled bar looks like the border and the filled part lights up. */
.exam-header { padding: 0; }
.exam-header > .container-fluid {
    display: flex;
    align-items: stretch;
    flex-wrap: nowrap;
    gap: 0;                    /* hairlines do the separating */
    min-height: 48px;
    padding: 0;
}
.exam-header .navbar-icon,
.exam-header .navbar-brand,
.exam-header .exam-cell {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: 0 .85rem;
    border-left: 1px solid rgba(255, 255, 255, .16);
    font-size: .875rem;
    white-space: nowrap;
    text-shadow: none;
}
.exam-header > .container-fluid > :first-child { border-left: 0; }
.exam-header .navbar-icon {
    padding: 0 .8rem;
    font-size: 1.15rem;
    line-height: 1;
    color: #fff;
    text-decoration: none;
}
button.navbar-icon { border: 0; background: transparent; font-family: inherit; }
/* Hover and focus are two rules rather than one selector list: an engine
   that does not know :focus-visible (Safari before 15.4, Chrome before 86)
   throws out the entire list containing it, which would take the hover
   state down with it. */
.exam-header .navbar-icon:hover {
    color: #fff;
    background: rgba(255, 255, 255, .12);
    outline: none;
}
.exam-header .navbar-icon:focus-visible {
    color: #fff;
    background: rgba(255, 255, 255, .12);
    outline: none;
}
.exam-header .navbar-icon-right { margin-left: auto; }
.exam-header .navbar-brand { margin: 0; font-weight: 600; }
.exam-header .navbar-brand .exam-name { color: rgba(255, 255, 255, .65); font-weight: 400; }
.reference-body { font-size: .875rem; }
.reference-body .nav-tabs { margin-bottom: 1rem; }
.reference-body table { width: 100%; margin-bottom: 1rem; }
.reference-body td { padding: .15rem .5rem; vertical-align: top; }

.exam-header .exam-status {
    display: flex;
    align-items: stretch;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}
.exam-header .exam-status strong { color: #fff; font-weight: 600; }
.exam-header .exam-key { color: rgba(255, 255, 255, .65); font-weight: 400; }
.exam-header .exam-count { font-variant-numeric: tabular-nums; }
.exam-header .exam-count strong { font-size: 1rem; }
.exam-header .exam-spacer { flex: 1 1 auto; }
.exam-header .exam-status .badge {
    background: #fff;
    color: var(--spu-brand);
    font-size: .7rem;
}

.exam-header .exam-due {
    display: flex;
    align-self: center;
    align-items: center;
    gap: .5rem;
    margin: 0 .5rem;
    padding: .25rem .35rem .25rem .7rem;
    border: 1px solid rgba(255, 255, 255, .45);
    border-radius: 999px;
    background: rgba(255, 255, 255, .1);
    color: #fff;
    font-size: .8125rem;
    font-weight: 600;
    white-space: nowrap;
}
.exam-header .exam-due .exam-key { color: rgba(255, 255, 255, .85); }
.exam-header .exam-countdown {
    padding: 2px 7px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .14);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.exam-header .exam-countdown.is-warning  { color: #ffd166; }
.exam-header .exam-countdown.is-critical { color: #ff8a80; }
/* Phones: the pill is label-less and short-dated already; tighten it. */
@media (max-width: 575.98px) {
    .exam-header .exam-due { margin: 0 .25rem; padding: .2rem .3rem .2rem .55rem; gap: .35rem; font-size: .75rem; }
    .exam-header .exam-countdown { padding: 1px 6px; }
}
.exam-progress {
    position: absolute;
    left: 0; right: 0; bottom: -4px;
    height: 4px;
    background: var(--spu-brand-dark);
}
.exam-progress-bar {
    height: 100%;
    width: 0;
    background: var(--spu-brand-light);
    box-shadow: 0 0 6px rgba(150, 196, 240, .8);
    transition: width .3s ease;
}

/* ---- Question ----------------------------------------------------------- */
.question-text { margin: .5rem 0 1.25rem; font-size: 1.05rem; line-height: 1.55; }
.question-text:focus { outline: none; }

/* ---- Question body: main column + optional image strip ------------------ */
/* The main column is the full width; only when a strip renders does it give
   up the strip's width, and the strip slides in from the right as it does. */
.exam-body { display: flex; align-items: stretch; }
.exam-main { flex: 1 1 auto; min-width: 0; }
.exam-body.has-strip .exam-main { padding-right: 1.5rem; }

/* Thumbnails, shared by the row layout and the strip. */
.question-images { margin-bottom: .5rem; }
.question-images > [class*="col-"] { margin-bottom: .75rem; }
.question-images .thumb,
.image-strip .thumb { position: relative; display: block; }
.question-images .thumb img,
.image-strip .thumb img { border: 1px solid #dee2e6; border-radius: .25rem; }
.question-images .thumb-zoom,
.image-strip .thumb-zoom {
    position: absolute;
    right: 6px; bottom: 6px;
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: 3px 8px;
    border-radius: .25rem;
    background: rgba(0, 0, 0, .65);
    color: #fff;
    font-size: .75rem;
    line-height: 1.4;
    pointer-events: none;
}

/* The film strip (config image_layout = 'strip'). */
.image-strip {
    flex: 0 0 188px;
    display: flex;
    flex-direction: column;
    gap: .625rem;
    padding: 1.125rem 1rem;
    background: #f6f8f7;
    border-left: 1px solid #e3e7ec;
    overflow: hidden;
    animation: strip-in .45s cubic-bezier(.2, .7, .2, 1) .12s both;
}
/* Fixed-width contents so they are revealed, not squeezed, while the strip grows. */
.image-strip > * { width: 156px; flex-shrink: 0; }
@keyframes strip-in {
    from { flex-basis: 0; padding-left: 0; padding-right: 0; opacity: 0; transform: translateX(32px); }
    to   { flex-basis: 188px; padding-left: 1rem; padding-right: 1rem; opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .image-strip { animation: none; }
}
.image-strip-label {
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: #6c757d;
}
.image-strip-hint { font-size: .75rem; color: #6c757d; }
.image-strip .thumb-zoom { padding: 2px 7px; font-size: .6875rem; }

/* Tablet portrait and phones: the strip becomes a horizontal one under the stem. */
@media (max-width: 991.98px) {
    .exam-body { display: block; }
    .exam-body.has-strip .exam-main { max-width: none; padding: .875rem .875rem 1.25rem; }
    .image-strip {
        flex-direction: row;
        align-items: center;
        gap: .625rem;
        border-left: 0;
        border-top: 1px solid #e3e7ec;
        overflow-x: auto;
        animation: strip-up .35s ease-out both;
    }
    .image-strip > * { width: auto; }
    .image-strip .thumb { flex: 0 0 132px; }
    .image-strip-hint { margin-left: auto; }
    @keyframes strip-up {
        from { opacity: 0; transform: translateY(12px); }
        to   { opacity: 1; transform: none; }
    }
}

/* ---- Answer choices ----------------------------------------------------- */
.answer-list { margin-bottom: .75rem; }
.answer-list .answer-item {
    display: flex;
    align-items: flex-start;
    gap: .6rem;
    padding: .6rem .9rem;
    margin-bottom: -1px;
    border: 1px solid #dee2e6;
    background: #fff;
    color: #212529;
    font-weight: normal;
    cursor: pointer;
    position: relative;
}
.answer-list .answer-item:first-child { border-radius: .375rem .375rem 0 0; }
.answer-list .answer-item:last-child  { border-radius: 0 0 .375rem .375rem; margin-bottom: 0; }
.answer-item .answer-radio {
    position: absolute;
    opacity: 0;
    width: 1px; height: 1px;
    margin: 0;
}
.answer-item .answer-letter {
    flex: 0 0 24px;
    width: 24px; height: 24px;
    border: 2px solid #8c959d;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .75rem;
    font-weight: 700;
    line-height: 1;
    color: #495057;
    background: #fff;
}
.answer-item .answer-text { flex: 1 1 auto; padding-top: 2px; }
.answer-item:hover { background: #f8f9fa; }
.answer-item:focus-within {
    outline: 3px solid #86b7fe;
    outline-offset: -3px;
    z-index: 2;
}
.answer-item.is-selected {
    background: #e3f0ff;
    border-color: #0d6efd;
    box-shadow: inset 4px 0 0 #0d6efd;
    z-index: 1;
}
.answer-item.is-selected .answer-letter {
    background: #0d6efd;
    border-color: #0d6efd;
    color: #fff;
}
.answer-item.is-locked { cursor: default; }
.answer-item.is-locked:hover { background: #fff; }

/* After an open-book answer */
.answer-item.is-correct {
    background: #d1e7dd;
    border-color: #146c43;
    box-shadow: inset 4px 0 0 #146c43;
    cursor: default;
    z-index: 1;
}
.answer-item.is-correct .answer-letter { background: #146c43; border-color: #146c43; color: #fff; }
.answer-item.is-wrong {
    background: #f8d7da;
    border-color: #b02a37;
    box-shadow: inset 4px 0 0 #b02a37;
    cursor: default;
    z-index: 1;
}
.answer-item.is-wrong .answer-letter { background: #b02a37; border-color: #b02a37; color: #fff; }
.answer-item.is-muted { color: #6c757d; cursor: default; }
.answer-item.is-muted .answer-letter { border-color: #ced4da; color: #adb5bd; }
.answer-item.is-correct:hover, .answer-item.is-wrong:hover, .answer-item.is-muted:hover { background: inherit; }

.feedback-panel .card-header { font-size: 1rem; }
.feedback-panel:focus { outline: none; }
.explanation-panel .card-body { line-height: 1.6; }

/* ---- Page transitions --------------------------------------------------- */
/* The script puts one of three classes on #myspan after each swap (see
   enter() in exam.js).  Only the main column moves; the header
   and the bottom bar stay put, so it reads as the question changing, not
   the page reloading.  While a request is in flight the outgoing column
   dims; the visible motion is all on entry, so a fast response costs no
   time and a failed one simply undims. */
#myspan.is-busy .exam-main,
#myspan.is-busy .answer-sheet,
#myspan.is-busy .end-card { opacity: .6; transition: opacity .15s ease; }

.enter-forward .exam-main,
.enter-forward .answer-sheet,
.enter-forward .end-card { animation: slide-forward .2s cubic-bezier(.2, .7, .2, 1) both; }
.enter-back .exam-main,
.enter-back .answer-sheet { animation: slide-back .2s cubic-bezier(.2, .7, .2, 1) both; }
@keyframes slide-forward {
    from { opacity: 0; transform: translateX(24px); }
    to   { opacity: 1; transform: none; }
}
@keyframes slide-back {
    from { opacity: 0; transform: translateX(-24px); }
    to   { opacity: 1; transform: none; }
}

/* The open-book reveal: the same question regraded.  The stem and choices
   do not move; the grading unfolds in reading order -- the chosen row first,
   then the correct one if it was another, then the verdict, then the
   explanation. */
.enter-reveal .answer-item.is-wrong   { animation: grade-in .25s ease-out both; }
.enter-reveal .answer-item.is-correct { animation: grade-in .25s ease-out .15s both; }
.enter-reveal .answer-item.is-correct .answer-letter,
.enter-reveal .answer-item.is-wrong .answer-letter { animation: letter-in .25s ease-out both; animation-delay: inherit; }
.enter-reveal .image-strip       { animation: none; }
.enter-reveal .feedback-panel    { animation: rise-in .25s ease-out .3s both; }
.enter-reveal .explanation-panel { animation: rise-in .3s ease-out .45s both; }
@keyframes grade-in {
    from { background-color: #fff; border-color: #dee2e6; box-shadow: inset 4px 0 0 transparent; }
}
@keyframes letter-in {
    from { background-color: #fff; border-color: #8c959d; color: #495057; }
}
@keyframes rise-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    #myspan.is-busy .exam-main,
    #myspan.is-busy .answer-sheet,
    #myspan.is-busy .end-card { transition: none; }
    .enter-forward .exam-main, .enter-forward .answer-sheet, .enter-forward .end-card,
    .enter-back .exam-main, .enter-back .answer-sheet,
    .enter-reveal .answer-item, .enter-reveal .answer-letter,
    .enter-reveal .feedback-panel, .enter-reveal .explanation-panel { animation: none; }
}

/* ---- Save status (on the chosen answer row) ---------------------------- */
.answer-item .answer-status {
    flex: 0 0 auto;
    margin-left: auto;
    padding-top: 3px;
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    font-size: .75rem;
    font-weight: 600;
    color: #6c757d;
    white-space: nowrap;
}
.answer-item .answer-status:empty { display: none; }
.answer-item .answer-status.is-saved { color: #146c43; }
.answer-item .answer-status.is-error { color: #b02a37; }
.answer-item .answer-status .btn-link {
    padding: 0;
    font-size: .75rem;
    font-weight: 600;
    color: #b02a37;
    text-decoration: underline;
    vertical-align: baseline;
}
.spinner {
    display: inline-block;
    width: 12px; height: 12px;
    border: 2px solid #ccc;
    border-top-color: #0d6efd;
    border-radius: 50%;
    animation: exam-spin .8s linear infinite;
    vertical-align: -2px;
}
@keyframes exam-spin { to { transform: rotate(360deg); } }

/* ---- Bottom action bar ------------------------------------------------- */
.exam-footer {
    background: #fff;
    border-top: 1px solid #c8d3df;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, .08);
    padding: 0;
}
.exam-footer .nav-justified .nav-link { padding: .9rem .6rem; font-weight: 600; }
.exam-actions {
    display: flex;
    gap: .5rem;
    padding: .6rem;
    max-width: 1140px;
    margin: 0 auto;
}
.exam-actions .btn {
    flex: 1 1 0;
    min-height: 44px;
    white-space: normal;
    position: relative;
    font-weight: 600;
    border-width: 2px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
}
.exam-actions .btn-review .badge {
    background: var(--spu-brand);
    color: #fff;
}
/* Every question answered: the Review button is the way to the results, so it
   fills with the brand colour and pulses a ring until it is pressed. */
.exam-actions .btn-review.is-complete {
    color: #fff;
    background-color: var(--spu-brand);
    border-color: var(--spu-brand);
    animation: review-glow 1.4s ease-out infinite;
}
.exam-actions .btn-review.is-complete .badge { background: #fff; color: var(--spu-brand); }
@keyframes review-glow {
    0%   { box-shadow: 0 0 0 0 rgba(var(--spu-brand-rgb), .7); }
    70%  { box-shadow: 0 0 0 12px rgba(var(--spu-brand-rgb), 0); }
    100% { box-shadow: 0 0 0 0 rgba(var(--spu-brand-rgb), 0); }
}
@media (prefers-reduced-motion: reduce) {
    .exam-actions .btn-review.is-complete { animation: none; box-shadow: 0 0 0 4px var(--spu-brand-light); }
}
.exam-actions .btn:focus-visible {
    outline: 3px solid var(--spu-brand-light);
    outline-offset: 2px;
    box-shadow: none;
}
.exam-actions .btn-jump { flex: 0 1 auto; }
.exam-actions .btn:disabled {
    color: #8a94a0;
    background-color: #f4f6f8;
    border-color: #c8d3df;
    opacity: 1;
    cursor: not-allowed;
}
.exam-actions .btn.is-loading { color: transparent !important; }
.exam-actions .btn.is-loading::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 18px; height: 18px;
    margin: -9px 0 0 -9px;
    border: 2px solid rgba(var(--spu-brand-rgb), .25);
    border-top-color: var(--spu-brand);
    border-radius: 50%;
    animation: exam-spin .8s linear infinite;
}
.exam-actions .btn-primary.is-loading::after { border-color: rgba(255, 255, 255, .4); border-top-color: #fff; }
@media (max-width: 575.98px) {
    .exam-actions { gap: .35rem; padding: .5rem .4rem; }
    .exam-actions .btn { padding-left: .4rem; padding-right: .4rem; }
}

/* ---- Review panel (slide-out) ------------------------------------------ */
.review-backdrop {
    position: fixed;
    top: 0; right: 0; bottom: 0; left: 0;
    background: rgba(0, 0, 0, .4);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s ease, visibility 0s linear .2s;
    z-index: 1040;
}
.review-panel {
    position: fixed;
    top: 0; right: 0; bottom: 0;
    width: 420px;
    max-width: 100%;
    background: #fff;
    box-shadow: -2px 0 12px rgba(0, 0, 0, .25);
    transform: translateX(100%);
    transition: transform .25s ease;
    overflow-y: auto;
    padding: 1rem;
    z-index: 1050;
}
.review-open .review-backdrop { opacity: 1; visibility: visible; transition-delay: 0s; }
.review-open .review-panel { transform: none; }
.review-panel h3 { margin-top: .25rem; font-weight: 400; }
.review-panel .btn-close { float: right; margin: .1rem 0 0 .5rem; }
.review-summary { margin-bottom: .75rem; color: #495057; }
.review-legend { font-size: .75rem; color: #6c757d; margin: .75rem 0; }
.review-legend .review-chip { width: 26px; height: 26px; font-size: .7rem; display: inline-flex; margin-right: 4px; vertical-align: middle; }

.review-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
    gap: 6px;
}
.review-chip {
    height: 44px;
    border: 1px solid #ced4da;
    border-radius: .25rem;
    background: #fff;
    color: #212529;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    padding: 0;
}
.review-chip.is-answered { background: #d1e7dd; border-color: #146c43; color: #146c43; }
.review-chip.is-unanswered { background: #fff; border-style: dashed; color: #6c757d; }
.review-chip.is-current { box-shadow: 0 0 0 3px #0d6efd; }
.review-chip:hover { filter: brightness(.95); }
.review-chip:focus-visible { outline: 3px solid #86b7fe; outline-offset: 1px; }
.review-finalize { margin-top: 1rem; }

/* ---- Full answer sheet page ------------------------------------------- */
.answer-sheet { max-width: 720px; margin: 0 auto; }
.answer-sheet .review-grid { grid-template-columns: repeat(auto-fill, minmax(52px, 1fr)); }

/* ---- Transport errors ---------------------------------------------------- */
#exam-alert {
    position: fixed;
    top: 60px;
    left: 50%;
    transform: translateX(-50%);
    width: calc(100% - 20px);
    max-width: 560px;
    z-index: 1060;
    box-shadow: 0 4px 12px rgba(0, 0, 0, .2);
}
#deadline-notice { z-index: 1061; }
#myspan.is-busy { cursor: progress; }

/* ---- Flex gap fallback --------------------------------------------------- */
/* `gap` on a *flex* container is Safari 14.1+, Chrome 84+, Firefox 63+, but
   the engine floor the exam actually runs on is far below that (see
   supported() in js/exam.js).  In that band every gap below
   collapses to zero: the letter bubble sits against its answer text, the
   action buttons touch, the header cells run together.  So exam.js
   measures a probe before the body paints and puts .no-flex-gap on <html>
   when gap does nothing; margins then stand in for it.
   @supports cannot do this job -- Safari 14 reports `gap' as supported
   because it has it for Grid, which is exactly the version that needs the
   fallback.

   Only the flex gaps are restated.  .review-grid is real Grid, where gap goes
   back to Safari 10.1 and needs nothing.  Add a rule here whenever a flex
   `gap' is added above, and delete this whole section once Safari 14 drops
   below the supported floor.

   Where every child is unconditionally visible, `> * + *' mirrors gap
   exactly.  Where Bootstrap's d-none/d-sm-inline hides one, the spacing goes
   on the named children instead, so a hidden sibling cannot leave a margin
   behind -- and .exam-header > .container-fluid is left alone because its gap
   is already 0. */

/* A bare text node inside a flex container becomes an anonymous flex item:
   gap separates it like any other, but no selector can reach it, and because
   `* + *' counts only elements the one after it is missed too.  Three
   containers here are an icon or a word followed by loose text -- the brand,
   the zoom pill, the save status -- so those are spaced from the element
   side with margin-right instead.  test_scripts/legacy-layout.html measures
   all of this; it is how the three were found. */

/* Top bar: icon and label within each cell, and the due pill's parts. */
.no-flex-gap .exam-header .navbar-icon > * + *,
.no-flex-gap .exam-header .exam-cell > * + * { margin-left: .4rem; }
.no-flex-gap .exam-header .exam-due > * + * { margin-left: .5rem; }
/* "SPU PURC" is loose text, so .exam-name is the brand's first element child
   and only a named rule reaches it. */
.no-flex-gap .exam-header .navbar-brand > .exam-name { margin-left: .4rem; }
@media (max-width: 575.98px) {
    .no-flex-gap .exam-header .exam-due > * + * { margin-left: .35rem; }
}

/* Image thumbnails and the film strip.  Only the grid's pill has a label
   after its icon; the strip's is the icon alone and wants no trailing gap. */
.no-flex-gap .question-images .thumb-zoom > .bi { margin-right: .25rem; }
.no-flex-gap .image-strip > * + * { margin-top: .625rem; }
@media (max-width: 991.98px) {
    /* The strip turns horizontal here, so the spacing turns with it. */
    .no-flex-gap .image-strip > * + * { margin-top: 0; margin-left: .625rem; }
}

/* Answer rows.  .answer-radio is position:absolute and so out of flow: gap
   never applied to it, and `> * + *' would wrongly indent the letter that
   follows it -- so only .answer-text is named.  .answer-status is already
   pushed to the far right by margin-left:auto and must keep it. */
.no-flex-gap .answer-list .answer-item > .answer-text { margin-left: .6rem; }
/* The save status is "<tick> Saved", "<spinner> Saving" or "Not saved
   <Retry>" -- an element on one side of loose text each time. */
.no-flex-gap .answer-item .answer-status > .bi,
.no-flex-gap .answer-item .answer-status > .spinner { margin-right: .25rem; }
.no-flex-gap .answer-item .answer-status > .btn-link { margin-left: .25rem; }

/* Bottom action bar.  Buttons are disabled rather than omitted, so every
   child is present and `> * + *' is safe; inside a button one of the two
   labels is always hidden, so only the badge is spaced. */
.no-flex-gap .exam-actions > * + * { margin-left: .5rem; }
.no-flex-gap .exam-actions .btn > .badge { margin-left: .35rem; }
@media (max-width: 575.98px) {
    .no-flex-gap .exam-actions > * + * { margin-left: .35rem; }
}

/* ---- No JavaScript / unsupported browser --------------------------------- */
/* Shown by the page's <noscript> or by showUnsupported() in exam.js.
   Neither case renders the fixed top and bottom bars, so the notice provides
   its own spacing rather than relying on the body padding meant for them. */
.exam-no-js { max-width: 640px; margin: 1.5rem auto; }
.exam-no-js .alert-heading { margin-bottom: .5rem; }

/* ---- End of exam / signed out -------------------------------------------- */
.end-card { max-width: 640px; margin-top: 2rem; }
.end-card .card-body { padding: 2rem 1.5rem; }
.end-icon { font-size: 3rem; line-height: 1; margin-bottom: .75rem; }
/* Wide enough for the longest pair this list holds: the "Questions answered"
   label against a full timestamp ("October 14, 2026 at 9:00 am").  At 380px
   both of those wrapped, which read as though the date were two values.  The
   gutter is tightened at the same time, because a .row spends 24px of every
   column on padding before any text is laid out.
   test_scripts/end-card.html measures it. */
.end-summary {
    --bs-gutter-x: 1rem;
    max-width: 460px;
    margin: 1.25rem auto 1.5rem;
    font-size: .95rem;
}
.end-summary dt { font-weight: 600; color: #6c757d; }
.end-summary dd { margin-bottom: .35rem; }
