html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

/* The template's two-ring focus in the brand blue (wwwroot/css/theme.css), on the surface colour so it reads in dark
   mode too; buttons show it for keyboard focus only, fields whenever they have focus. */
.btn:focus-visible, .btn-link.nav-link:focus-visible, .form-control:focus, .form-select:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem var(--mst-surface), 0 0 0 0.25rem var(--mst-ink);
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}
/* MVC validation classes (server-side ModelState and aspnet-client-validation both emit these),
   styled like Bootstrap's invalid state so client and server errors look identical. */
.input-validation-error {
  border-color: var(--bs-form-invalid-border-color);
}

.input-validation-error:focus {
  border-color: var(--bs-form-invalid-border-color);
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
}

.field-validation-error {
  display: block;
  margin-top: 0.25rem;
  font-size: 0.875em;
  color: var(--bs-form-invalid-color);
}

.validation-summary-errors {
  color: var(--bs-form-invalid-color);
}

.validation-summary-valid {
  display: none;
}

/* The eye buttons beside a password field (the login page's, Change Password's; wwwroot/js/password-toggle.js marks the
   form once they show) stand in for Edge's own reveal button, which would be a second eye in the field.
   docs/plans/authentication.md decision 11, docs/plans/change-password.md */
[data-password-toggles] input::-ms-reveal {
  display: none;
}

/* The What's New card (Views/Secure/_AnnouncementCard.cshtml): the author's line breaks kept, clamped to
   three lines until Read More adds .announcement-expanded to the card (wwwroot/js/announcement.js). */
.announcement-text {
  white-space: pre-line;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

.announcement-card.announcement-expanded .announcement-text {
  display: block;
  -webkit-line-clamp: none;
  overflow: visible;
}

/* The navbar search box (Views/Shared/_Layout.cshtml). Below lg it sits in the hamburger at full width; from lg it
   is a fixed-width box between the nav lists. 16 px minimum because iOS Safari zooms into smaller inputs and the
   root font is 14 px below 768 px. */
.site-search-input {
  font-size: max(1rem, 16px);
}

@media (min-width: 992px) {
  .site-search-input {
    width: 14rem;
  }
}

@media (min-width: 1200px) {
  .site-search-input {
    width: 18rem;
  }
}

.site-search-button {
  min-width: 2.75rem; /* a 44 px touch target */
}

/* The type-ahead list under the box (wwwroot/js/search.js): Bootstrap's dropdown look without the Dropdown
   plugin. Full width of the form in the hamburger, at least 18rem beside the nav links; journal snippets wrap;
   rows are 44 px tall for touch; the mark stays readable on the blue active row. */
.site-search-menu {
  width: 100%;
  min-width: 18rem;
  max-width: calc(100vw - 2rem);
  max-height: 60vh;
  overflow-y: auto;
}

.site-search-menu .dropdown-item {
  white-space: normal;
  overflow-wrap: anywhere;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  cursor: pointer;
}

.site-search-menu mark {
  padding: 0;
}

.site-search-menu .dropdown-item.active mark {
  color: inherit;
  background: transparent;
  text-decoration: underline;
}

/* The results page (Views/Search/Index.cshtml). The match is marked without Bootstrap's horizontal padding on
   <mark> so the word does not shift; the full entry keeps the line breaks the plain-text conversion emits for
   paragraphs and <br>; Show more / Show less follow the Bootstrap collapse state with no script of their own. */
.search-hit mark {
  padding: 0;
}

.search-full-text {
  white-space: pre-line;
}

[data-bs-toggle="collapse"][aria-expanded="true"] .search-when-collapsed,
[data-bs-toggle="collapse"][aria-expanded="false"] .search-when-expanded {
  display: none;
}

/* A reading or prayer page (Views/ReadingsAndPrayers/Show.cshtml): the app's reading size and line spacing, and a
   paragraph's own line breaks kept (the Prayer of St. Francis's lines); the citation sits under the last paragraph. */
.passage-text {
  white-space: pre-line;
  font-size: 1.125rem;
  line-height: 1.65;
}

.passage-text:last-child {
  margin-bottom: 0;
}

/* The apps' yellow fade after a save (iOS systemYellow at the app's 0.28 opacity): the sobriety card
   (wwwroot/js/sobriety-form.js) and a Spot Check row (wwwroot/js/spot-check.js) carry the colour and come back to the
   surface. It plays under prefers-reduced-motion too: a colour crossfade moves nothing, and Windows' "Animation
   effects" toggle (off on Nathan's machine) puts Chrome, Edge and Firefox under reduce, which is where an earlier
   animation: none had hidden the fade from him (2026-09-09; the legacy page's jQuery UI highlight never asked). */
@keyframes mst-flash {
  0% { background-color: var(--mst-surface); }
  14% { background-color: rgba(255, 204, 0, 0.28); }
  68% { background-color: rgba(255, 204, 0, 0.28); }
  100% { background-color: var(--mst-surface); }
}

.mst-flash {
  animation: mst-flash 1.85s ease-in-out 1;
}

/* The sobriety card (Views/Secure/_SobrietyCard.cshtml, wwwroot/js/sobriety.js): the four rows like the iOS stats
   list, tabular digits so a changing count does not wobble. */
.sobriety-stats li {
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--bs-border-color);
  font-size: 1.125rem;
}

.sobriety-stats li:last-child {
  border-bottom: 0;
}

.sobriety-stats strong {
  font-variant-numeric: tabular-nums;
}

/* The card's "Tell Your Friends!" block under the rows (data-sobriety-share, unhidden by sobriety.js once it has the
   numbers): the app's divider between the Hours row and the button. The last row drops its own border and the block
   carries it, so a card with the block hidden looks as it did. */
.sobriety-share {
  border-top: 1px solid var(--bs-border-color);
}

/* The sidebar (Views/Shared/_SideNav.cshtml, _SideNavItems.cshtml): the legacy nav-list well as a card holding the
   active tab's own list, always fully expanded and identical on every page of the tab. Its head carries the tab glyph
   in bold; under it, group headings in the legacy nav-header look (small, bold, uppercase, secondary) and rows with
   the app's row icons (theme.css .mst-tile, .mst-ring, .mst-glyph), every one indented by its depth (--side-nav-depth,
   set by the partial: 1.5 rem at the first level and 1 rem more per level, so a heading starts where its rows' icons
   do, as iOS's grouped lists have it). The rows are a little tighter than the pages' (the lists grow). The card stands
   in the page flow and scrolls with the page: the sticky, self-scrolling card of 2026-09-09 grew its own scrollbar in
   a short window and jumped on every page load. From lg the offcanvas body is block, not Bootstrap's from-lg flex,
   which would shrink the card to its text; below lg Bootstrap's offcanvas rules apply (fixed, 400 px, hidden until
   opened) and the drawer's own body scrolls. The active row's fill is theme.css's --mst-fill. */
.side-nav-row,
.side-nav-heading {
  padding-left: calc(1.5rem + (var(--side-nav-depth, 1) - 1) * 1rem);
}

.side-nav-row {
  padding-top: 0.375rem;
  padding-bottom: 0.375rem;
}

.side-nav-caption {
  color: var(--bs-secondary-color);
}

.side-nav-row.active .side-nav-caption {
  color: inherit;
  opacity: 0.85;
}

.side-nav-heading {
  margin: 0;
  padding-top: 0.75rem;
  padding-bottom: 0.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bs-secondary-color);
}

@media (min-width: 992px) {
  .side-nav .offcanvas-body {
    display: block;
  }
}

@media print {
  .side-nav {
    display: none;
  }
}

/* The Spot Check Inventory page (Views/SpotCheck/Index.cshtml, _SpotCheckRow.cshtml, wwwroot/js/spot-check.js). A
   value is the member's text with its line breaks kept (a textarea's), wrapping anywhere so a long word cannot widen
   the column. Once the script has made it a button it keeps the text's own look (the type badge inside stays a badge)
   and shows a pencil with a tint on hover and keyboard focus, never a border, so the list reads as a list; where
   there is no hover (a touch screen) the pencil is always faintly there. The editor stands in the value's place. The
   controls are not for paper. */
.spot-check-value {
  white-space: pre-line;
  overflow-wrap: anywhere;
}

button.spot-check-value {
  display: block;
  width: 100%;
  margin: -0.125rem -0.25rem;
  padding: 0.125rem 0.25rem;
  border: 0;
  border-radius: var(--bs-border-radius-sm);
  background: none;
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

button.spot-check-value:hover,
button.spot-check-value:focus-visible {
  background-color: var(--bs-tertiary-bg);
}

button.spot-check-value:focus-visible {
  outline: 2px solid var(--mst-ink);
  outline-offset: 1px;
}

.spot-check-pencil {
  opacity: 0;
  color: var(--mst-ink);
  vertical-align: -0.125em;
}

button.spot-check-value:hover .spot-check-pencil,
button.spot-check-value:focus-visible .spot-check-pencil {
  opacity: 1;
}

@media (hover: none) {
  .spot-check-pencil {
    opacity: 0.6;
  }
}

.spot-check-editor {
  margin-top: 0.25rem;
}

@media print {
  .spot-check-actions,
  .spot-check-create,
  .spot-check-pencil,
  .spot-check [data-share-status],
  [data-spot-check-add],
  [data-spot-check-more] {
    display: none !important;
  }
}

/* The type badge's button stays inline after the weekday ("Monday - Resentment", the phone's one line) and keeps
   the badge's own size; the other values are blocks that fill their column. */
button.spot-check-value[data-field="Type"] {
  display: inline-block;
  width: auto;
  margin: 0;
  padding: 0 0.125rem;
  vertical-align: baseline;
}

/* The Journal page (Views/Journal/Index.cshtml, _JournalRow.cshtml, wwwroot/js/journal.js). An entry is the sanitized
   HTML the service returns (paragraphs, lists, links), wrapping anywhere so a long word cannot widen the card; its
   last paragraph loses its margin so the row's padding is the spacing. Once the script has made the block an Edit
   control it keeps the text's own look and shows a pencil with a tint on hover and keyboard focus, never a border;
   where there is no hover (a touch screen) the pencil is always faintly there. The editor stands in the entry's
   place. The controls are not for paper. */
.journal-entry {
  overflow-wrap: anywhere;
}

.journal-entry > :last-child {
  margin-bottom: 0;
}

.journal-value[role="button"] {
  position: relative;
  margin: -0.125rem -0.25rem;
  padding: 0.125rem 0.25rem;
  border-radius: var(--bs-border-radius-sm);
  cursor: pointer;
}

.journal-value[role="button"]:hover,
.journal-value[role="button"]:focus-visible {
  background-color: var(--bs-tertiary-bg);
}

.journal-value[role="button"]:focus-visible {
  outline: 2px solid var(--mst-ink);
  outline-offset: 1px;
}

.journal-edit-hint {
  position: absolute;
  top: 0.125rem;
  right: 0.25rem;
  color: var(--mst-ink);
  opacity: 0;
}

.journal-value[role="button"]:hover .journal-edit-hint,
.journal-value[role="button"]:focus-visible .journal-edit-hint {
  opacity: 1;
}

@media (hover: none) {
  .journal-edit-hint {
    opacity: 0.6;
  }
}

.journal-editor {
  margin-top: 0.25rem;
}

@media print {
  .journal-actions,
  .journal-create,
  .journal-edit-hint,
  .journal [data-share-status],
  [data-journal-add],
  [data-journal-more] {
    display: none !important;
  }
}

/* Quill's snow theme (wwwroot/lib/quill/dist/quill.snow.css) in the site's colours: Bootstrap's form-control
   surface, text and border, so the editor follows dark mode (data-bs-theme) as the inputs do; the toolbar's icons
   take the body colour and the ink on hover, as Quill's own hover blue would clash with the brand. The editing area
   is as tall as the create form's textarea was. */
.journal-quill .ql-toolbar.ql-snow {
  border-color: var(--bs-border-color);
  border-radius: var(--bs-border-radius) var(--bs-border-radius) 0 0;
  background-color: var(--bs-tertiary-bg);
}

.journal-quill .ql-container.ql-snow {
  border-color: var(--bs-border-color);
  border-radius: 0 0 var(--bs-border-radius) var(--bs-border-radius);
  background-color: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-family: inherit;
  font-size: 1rem;
}

.journal-quill .ql-editor {
  min-height: 12rem;
  line-height: var(--bs-body-line-height);
}

.journal-quill .ql-editor.ql-blank::before {
  color: var(--bs-secondary-color);
  font-style: normal;
}

.journal-quill .ql-snow .ql-stroke {
  stroke: var(--bs-body-color);
}

.journal-quill .ql-snow .ql-fill {
  fill: var(--bs-body-color);
}

.journal-quill .ql-snow.ql-toolbar button:hover .ql-stroke,
.journal-quill .ql-snow.ql-toolbar button:focus .ql-stroke,
.journal-quill .ql-snow.ql-toolbar button.ql-active .ql-stroke {
  stroke: var(--mst-ink);
}

.journal-quill .ql-snow.ql-toolbar button:hover .ql-fill,
.journal-quill .ql-snow.ql-toolbar button:focus .ql-fill,
.journal-quill .ql-snow.ql-toolbar button.ql-active .ql-fill {
  fill: var(--mst-ink);
}

.journal-quill .ql-snow .ql-tooltip {
  background-color: var(--bs-body-bg);
  color: var(--bs-body-color);
  border-color: var(--bs-border-color);
  box-shadow: var(--bs-box-shadow-sm);
}

.journal-quill .ql-snow .ql-tooltip input[type="text"] {
  background-color: var(--bs-body-bg);
  color: var(--bs-body-color);
  border-color: var(--bs-border-color);
}

.journal-quill .ql-snow .ql-tooltip a.ql-action,
.journal-quill .ql-snow .ql-tooltip a.ql-remove,
.journal-quill .ql-snow .ql-tooltip a.ql-preview {
  color: var(--mst-ink);
}

/* The editor's focus ring, as a form-control's. */
.journal-quill .ql-container.ql-snow:focus-within {
  border-color: var(--bs-focus-ring-color);
  box-shadow: 0 0 0 var(--bs-focus-ring-width) var(--bs-focus-ring-color);
}

/* The error state the validator gives an input, on the editor that stands in for the textarea. */
.journal-quill.input-validation-error .ql-container.ql-snow,
.journal-quill.input-validation-error .ql-toolbar.ql-snow {
  border-color: var(--bs-form-invalid-border-color);
}

/* The Nightly Inventory page (Views/NightlyInventory/Index.cshtml, _NightlyInventoryRow.cshtml,
   wwwroot/js/nightly-inventory.js). A row lists the ten questions as a definition list: the prompt, then "Yes" or
   "No" and the description, which keeps its line breaks and wraps anywhere so a long word cannot widen the card. The
   editor stands in the answers' place; the create form's and the editor's Yes / No pair is Bootstrap's button-radio.
   The controls are not for paper. */
.nightly-inventory-answers dt {
  margin-top: 0.5rem;
}

.nightly-inventory-answers dt:first-child {
  margin-top: 0;
}

.nightly-inventory-answers dd {
  margin-bottom: 0;
}

.nightly-inventory-describe {
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.nightly-inventory-question legend {
  float: none;
  width: auto;
}

.nightly-inventory-editor {
  margin-top: 0.25rem;
}

.nightly-inventory-email-body {
  white-space: pre-wrap;
}

@media print {
  .nightly-inventory-actions,
  .nightly-inventory-create,
  .nightly-inventory [data-share-status],
  [data-ni-add],
  [data-ni-more] {
    display: none !important;
  }
}

/* The Gratitude List page (Views/Gratitude/Index.cshtml, _GratitudeRow.cshtml, wwwroot/js/gratitude.js). A row
   lists the boxes that were filled in with their numbers; a gratitude keeps its line breaks and wraps anywhere so a
   long word cannot widen the card. The create form's and the editor's ten boxes are input groups with the number
   before each textarea. The controls are not for paper. */
.gratitude-items li {
  margin-top: 0.25rem;
}

.gratitude-items li:first-child {
  margin-top: 0;
}

.gratitude-text {
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.gratitude-box .input-group-text {
  align-items: flex-start;
  min-width: 2.75rem;
  justify-content: flex-end;
}

.gratitude-editor {
  margin-top: 0.25rem;
}

/* The phone's "Autosaved · 3:47 PM" (GratitudeSaveView, monospaced digits): the line over the boxes keeps its height
   while it is empty, so the boxes never jump when the words appear. */
[data-gl-autosaved] {
  font-variant-numeric: tabular-nums;
}

/* The Journal page's (JournalSaveView, docs/plans/journal.md decision 16): the line left of the count keeps its
   height while it is empty, so the messages under it and the Save bar never jump when the words appear. */
[data-journal-autosaved] {
  font-variant-numeric: tabular-nums;
}

.journal-autosaved {
  display: block;
  min-height: 1.5em;
}

.gratitude-autosaved {
  min-height: 1.5em;
}

.gratitude-email-body {
  white-space: pre-wrap;
}

@media print {
  .gratitude-actions,
  .gratitude-create,
  .gratitude [data-share-status],
  [data-gl-add],
  [data-gl-more] {
    display: none !important;
  }
}

/* The Email Nathan form's trap field (docs/plans/contact.md decision 4): kept in the document for a form-filling script to
   find, but off-screen, unreachable by keyboard (tabindex -1) and hidden from readers (aria-hidden); never display:none,
   which such scripts skip. */
.contact-trap {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The Premium Membership page (docs/plans/premium-membership.md): the phone's plan cards, ringed in the brand blue when
   chosen (the radio's own state, so the ring holds with JavaScript off; premium.js adds the class for browsers without
   :has), its tinted feature icons, and its gold "Premium Member" pill (SettingsView's capsule: gold text on gold at 15%). */
.premium-plan {
  cursor: pointer;
  border: 2px solid var(--bs-border-color);
}

.premium-plan:has(input:checked),
.premium-plan.premium-plan-selected {
  border-color: var(--bs-primary);
}

.premium-plan:focus-within {
  outline: 2px solid var(--bs-primary);
  outline-offset: 2px;
}

.premium-feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  color: var(--bs-indigo);
  background-color: rgba(102, 16, 242, 0.15);
}

.premium-badge {
  color: #b48a1e;
  background-color: rgba(217, 166, 38, 0.15);
}

[data-bs-theme="dark"] .premium-badge {
  color: #e2b93b;
}

/* The hardship discount (docs/plans/hardship-discount.md): the paywall's "I Need a 100% Hardship Discount" button as the
   phone draws it (UnlimitedPaywallStep1View: red semibold text on pink at 12%), and the Premium page's row icon as the
   phone's purple heart in a purple circle at 15% (UnlimitedPaywallStep2View). Both tints are translucent, so dark mode
   keeps them. */
.hardship-btn {
  color: var(--bs-danger);
  background-color: rgba(255, 45, 85, 0.12);
  border-color: transparent;
}

.hardship-btn:hover,
.hardship-btn:focus-visible {
  color: var(--bs-danger);
  background-color: rgba(255, 45, 85, 0.2);
  border-color: transparent;
}

.hardship-row {
  color: inherit;
  text-decoration: none;
}

.hardship-row:hover,
.hardship-row:focus-visible {
  border-color: var(--bs-purple);
}

.hardship-row-title {
  color: var(--bs-purple);
}

.hardship-row-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  color: var(--bs-purple);
  background-color: rgba(111, 66, 193, 0.15);
}

/* The apps' "New" pill (Swift: caption2 semibold white on Color.blue in a capsule; Kotlin NavRow's badge chip) at the
   end of a recently added row on the Settings page and in the sidebar: white on the fill ink, kept in dark mode. On
   the lit sidebar row the fill is the same ink, so the pill inverts there and stays readable. */
.new-badge {
  color: #fff;
  background-color: var(--mst-fill);
  font-weight: 600;
}

.list-group-item.active .new-badge {
  color: var(--mst-fill);
  background-color: #fff;
}

/* The admin's error page (Views/Admin/Error.cshtml): the stored mail body, whose tables, headings and pre the mail
   leaves bare. Long values (a URL, a stack frame, a JSON body) wrap inside the card rather than widening the page. */
.error-details table {
  margin-bottom: 1rem;
}

.error-details th {
  padding-right: 1rem;
  vertical-align: top;
  white-space: nowrap;
}

.error-details td {
  word-break: break-all;
}

.error-details h3 {
  font-size: 1.1rem;
  margin-top: 1.25rem;
}

.error-details pre {
  color: var(--bs-body-color);
  white-space: pre-wrap;
  word-break: break-word;
}

/* The development-only Requests page (Views/Dev/Requests.cshtml): a row opens to its bodies, which wrap inside the
   table rather than widening the page. */
.dev-requests-row {
  cursor: pointer;
}

.dev-requests-body {
  color: var(--bs-body-color);
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 24rem;
  overflow: auto;
}
