/* Shared styles for the six public marketing pages (home, company profile,
   FAQ, pricing, privacy policy, terms).

   Why this file exists: those six pages are standalone -- they do not extend
   base.html and never link style.css -- so each one carried its own <style>
   block, and the rules below were duplicated byte-for-byte across all six.

   Anything genuinely common to the public pages belongs here. Page-specific
   rules stay in that page's own <style> block, which is still loaded after
   this file and therefore still wins on conflict.

   NOTE: this is intentionally NOT style.css. That stylesheet is the signed-in
   application's (sidebar, panels, forms) and pulling ~1000 lines of it onto a
   marketing page would risk more than it fixes. The two systems stay separate
   in their *component* rules; this file is the public half.

   What DID get unified: the design tokens. This file used to open with its own
   :root block declaring --primary: #0d6e6e, while style.css declared
   --primary: #1E9A83 -- two different brand teals, so the homepage and the
   dashboard it sold you were visibly different colors. It also carried
   --primary-light: #e6f4f4, its own near-duplicate of the app's
   --primary-tint: #E7F5F0. All of that now comes from static/css/tokens.css,
   loaded immediately before this file on every public page, with #1E9A83 as
   the canonical brand hue. --primary-light and --text/--text-muted/--card
   still work -- tokens.css keeps them as aliases so the six templates' own
   style blocks did not all have to be rewritten at once.

   One thing to know before using the teal here: --primary (#1E9A83) is
   decorative only. It is too light to carry white text (3.50:1) or to BE
   text on a light background (also 3.50:1). Use --primary-strong for a filled
   surface under white text, --primary-text for teal words, --primary-deep for
   headings. tokens.css explains the full ramp. */

* { box-sizing: border-box; margin: 0; padding: 0; }

/* ---------- The shell's own base styles ----------
   Hoisted out of the six page <style> blocks when public_base.html was
   introduced. Every rule below was byte-identical in the five or six pages
   that declared it -- verified before moving, not assumed.

   Position matters: these are unconditional rules, so they sit ABOVE the
   @media blocks further down. Appending them at the end of the file instead
   would have let .topnav-links here override the mobile .topnav-links inside
   @media (max-width: 760px) at equal specificity, quietly breaking the
   hamburger menu. (Which is what the first draft of this change did.)

   Two rules keep a page-level override rather than being averaged, so nothing
   moves by a pixel:

     * body line-height is 1.6 here and 1.65 on privacy_policy and terms.
       Those two are long legal prose and were set looser on purpose.
     * .topnav-links gap is 1.4rem here and 0.9rem on the homepage, which
       carries an extra "Log in" item in the list and needs the room.

   And one page now states a value it previously left unset:
   company_profile declares .brand-kicker { margin-bottom: 0 } to keep its
   existing spacing, because it was the one page whose copy of that rule had
   no margin-bottom. */
/* DIRECTION A / THE LEDGER (10 Sep 2026). See docs/PUBLIC_SITE_DESIGN.md.
   Two rules do most of the work and both are here rather than per page:
   prose is a serif on warm paper, and anything the INTERFACE says (nav,
   headings, buttons, labels, table headers) is the grotesque. A page that
   sets its own font-family is almost certainly a mistake now. */
body {
  font-family: var(--font-public-prose);
  background: var(--paper);
  color: var(--ink);
  font-size: 1.0625rem;   /* 17px -- a serif needs the extra pixel */
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5 {
  font-family: var(--font-public-display);
  letter-spacing: -0.02em;
  /* 1.62 is right for a serif paragraph and much too loose for a heading --
     the body value was being inherited, which left every h2 on the site
     looking like two unrelated lines. */
  line-height: 1.16;
  text-wrap: balance;
  color: var(--ink);
}
h1 { line-height: 1.06; }
a { color: var(--primary-text); }

.topnav {
  background: var(--paper);
  border-bottom: 1px solid var(--rule-warm);
  position: sticky;
  top: 0;
  z-index: 10;
}
.topnav-links { display: flex; gap: 1.4rem; flex-wrap: wrap; }
/* The current page's nav item. Previously declared only by pricing.html, which
   was also the only page that set class="active" -- public_base.html now
   derives that from request.endpoint for every page, so the rule belongs here. */
.topnav-links a.active { color: var(--primary-deep); }

/* WAS A TEAL PILL WITH 0.12em TRACKING, on five pages. That badge is one of
   the half-dozen things that made the site read as generated -- every
   template-built landing page opens with one. It is now a plain overline: same
   markup, no background, no radius, so all five pages changed with this rule
   and none of them needed editing. */
.brand-kicker {
  display: block;
  font-family: var(--font-public-display);
  color: var(--muted);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 0.7rem;
}

/* Left-aligned, like everything else now. A centred footer was the last
   place the old habit survived. */
.site-footer {
  max-width: 1040px;
  margin: 0 auto;
  padding: 1.6rem 1.25rem 2.4rem;
  border-top: 1px solid var(--rule-warm);
  color: var(--muted);
  font-family: var(--font-public-display);
  font-size: 0.82rem;
  display: flex;
  gap: 0.6rem 1.4rem;
  align-items: baseline;
  flex-wrap: wrap;
  justify-content: space-between;
}
.site-footer a { color: var(--primary-text); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; }
.site-footer .footer-links { margin-top: 0.4rem; }

/* ---------- Skip link ----------
   Needs its own copy here: the public pages never load style.css, so the
   identical rule over there does not reach them. Same reason public.css
   already carries its own prefers-reduced-motion block.

   Off-screen until focused rather than clipped, so it can actually be seen and
   read once it takes focus. The topnav puts the logo plus six links ahead of
   the page content on every one of these pages. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--primary-strong); color: #fff;
  padding: 0.7rem 1.1rem; border-radius: 0 0 8px 0;
  font-size: 0.9rem; font-weight: 600; text-decoration: none;
}
.skip-link:focus {
  left: 0;
  outline: 2px solid var(--primary-dark); outline-offset: 2px;
}
.skip-link:hover { text-decoration: underline; }

.topnav-inner {
      max-width: 900px;
      margin: 0 auto;
      padding: 0.85rem 1.25rem;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 1rem;
      flex-wrap: wrap;
    }

.topnav-brand {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      text-decoration: none;
      font-family: var(--font-public-display);
      font-weight: 700;
      letter-spacing: -0.03em;
      color: var(--ink);
      font-size: 1.14rem;
    }

.topnav-links a {
      color: var(--muted);
      text-decoration: none;
      font-family: var(--font-public-display);
      font-size: 0.86rem;
      font-weight: 600;
    }

.topnav-links a:hover { color: var(--ink); }
.topnav-links a.active { color: var(--ink); }

/* Ink, not teal, and a 2px radius rather than a pill (direction A). The teal
   is spent on links and on the one figure that matters, not on every button --
   which is what stops the palette reading as a template. Paper on --ink is
   16.14:1, so this is the highest-contrast control on the page, which is
   right for the page's one conversion action. */
.topnav-login {
      background: var(--ink);
      color: var(--paper) !important;
      text-decoration: none;
      font-family: var(--font-public-display);
      font-weight: 700;
      font-size: 0.84rem;
      padding: 0.55rem 1.1rem;
      border-radius: 2px;
      white-space: nowrap;
    }

.topnav-login:hover { background: var(--primary-dark); }

/* ---------- Mobile nav toggle ---------------------------------------------
   The public topnav is a logo, six links (Product, Security, Pricing, FAQ,
   Company, Log in) and a Sign Up button in one flex row. Until now the only
   concession to phone-width screens was shrinking the gap between links at
   600px, so all eight items stayed on screen and simply wrapped into three
   or four stacked rows -- pushing the hero well below the fold on the page
   that has to do the selling.

   This is the same pattern the signed-in app's sidebar already uses and has
   already had its bugs shaken out of (see .nav-toggle in style.css and the
   script at the bottom of base.html): a button that toggles one class, with
   all the actual show/hide in CSS, and max-height rather than display so the
   reveal can animate. The button is hidden above the breakpoint, so nothing
   changes on desktop.

   Keyboard/AT behavior comes from using a real <button> with aria-controls
   and aria-expanded in the markup -- the script's only job is keeping
   aria-expanded in sync with the class. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 4px;
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  background: none;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  padding: 0;
}
.nav-toggle:hover { background: var(--primary-tint); }
.nav-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.nav-toggle-bar {
  display: block;
  width: 20px;
  height: 2px;
  border-radius: 2px;
  background: var(--primary-deep);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
/* Hamburger morphs into an X while the menu is open. */
.topnav.nav-open .nav-toggle-bar:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.topnav.nav-open .nav-toggle-bar:nth-child(2) { opacity: 0; }
.topnav.nav-open .nav-toggle-bar:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 760px) {
  .nav-toggle { display: flex; }

  /* Brand on the left, toggle on the right, everything else collapsed onto
     its own full-width row below them. */
  .topnav-inner { flex-wrap: wrap; gap: 0; }
  .topnav-brand { flex: 1; min-width: 0; }

  .topnav-links {
    flex-basis: 100%;
    flex-direction: column;
    gap: 0;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.25s ease;
  }
  /* Generous row height for touch: 44px is the WCAG 2.2 / iOS HIG minimum
     target, and these were previously ~20px inline text links sitting a few
     pixels apart -- easy to mis-tap. */
  .topnav-links a {
    display: block;
    padding: 0.8rem 0.25rem;
    min-height: 44px;
    font-size: 1rem;
    border-bottom: 1px solid var(--rule-warm);
  }

  /* The Sign Up button is the one item that does NOT hide behind the toggle
     -- it is the page's primary conversion action, so it stays visible at
     all widths, just moved onto its own full-width row. */
  .topnav-login {
    flex-basis: 100%;
    text-align: center;
    margin-top: 0.75rem;
    padding: 0.75rem 1.1rem;
    min-height: 44px;
    font-size: 0.95rem;
  }

  .topnav.nav-open .topnav-links { max-height: 26rem; margin-top: 0.5rem; }
}

/* The app's stylesheet disables all transitions under prefers-reduced-motion;
   this file needs its own copy of that rule because the public pages never
   load style.css. Covers the hamburger bars and the menu's max-height slide
   above -- with motion reduced the menu snaps open instead of animating. */
@media (prefers-reduced-motion: reduce) {
  .nav-toggle-bar,
  .topnav-links { transition: none !important; }
}

/* ===========================================================================
   DIRECTION A / THE LEDGER -- the shared component layer (10 Sep 2026)

   Everything below replaces one habit: the card grid. Nine identical bordered,
   rounded, soft-shadowed tiles was the homepage's whole structural vocabulary,
   and nine equal cards is not a hierarchy -- it told a dentist that patient
   charting and the built-in help guide were equally important.

   So border, fill, radius and shadow are now SPENT, not stamped. A hairline
   rule separates things that belong to one list; a border appears only where
   something really is a separate object (the product frames, the price panel).
   Nothing on these pages carries a shadow any more.

   docs/PUBLIC_SITE_DESIGN.md records the rest of the rules, including the one
   that matters most: nothing is centred.
   ======================================================================== */

.wrap { max-width: 1040px; margin: 0 auto; padding-inline: 1.25rem; }
/* Every direct child of a .wrap grid is allowed to shrink, for the reason
   spelled out on .table-scroll below. */
.wrap > * { min-width: 0; }

/* The page header on FAQ, pricing, the privacy policy, the terms and the
   deletion page. Five templates each carried their own copy of this, all five
   `text-align: center` with a 560-640px subline underneath -- one of the six
   things that made the site read as generated. Defined once here, left, and
   the five copies were deleted rather than overridden. */
.brand-header { padding: 2.4rem 0 1.8rem; border-bottom: 1px solid var(--rule-warm); margin-bottom: 2rem; }
.brand-header h1 { font-size: clamp(1.6rem, 4vw, 2.2rem); font-weight: 700; margin-bottom: 0.5rem; }
.brand-header p { font-size: 1.05rem; color: var(--muted); max-width: 62ch; }
.brand-header .updated { font-family: var(--font-public-display); font-size: 0.8rem; color: var(--muted); }
.wrap-narrow { max-width: 68ch; }

/* A section is separated by air and, where it helps, one rule. `alt` is a
   white band on the warm ground -- the only device used to group sections,
   replacing the old bordered-and-shadowed "section.alt" card look. */
.sec { padding-block: 3.4rem; }
.sec + .sec { border-top: 1px solid var(--rule-warm); }
.sec-alt { background: var(--surface); border-block: 1px solid var(--rule-warm); }

/* LEFT, not centre. The old rule was `text-align: center` with a 560px muted
   subline under every heading on the site. */
.section-heading { text-align: left; margin-bottom: 1.6rem; max-width: 62ch; }
.section-heading h2 { font-size: clamp(1.4rem, 3vw, 1.75rem); font-weight: 700; margin-bottom: 0.5rem; }
.section-heading p { color: var(--muted); font-size: 1rem; }
.section-heading .brand-kicker { margin-bottom: 0.5rem; }

/* --- Grafts from direction B ----------------------------------------------
   Direction A was chosen (a dentist's feedback) and B was left on the shelf,
   but docs/PUBLIC_SITE_DESIGN.md kept two of its ideas as worth stealing: an
   edge-to-edge trust strip, and an honestly EMPTY quote slot. Both landed
   2026-09-10. Neither borrows B's type or palette -- Bitter, Karla and clay
   orange stay unbuilt; these are B's structure said in A's voice.

   The strip is a band, not three cards. It sits directly under the hero
   because the three things on it are the objections a dentist raises before
   reading a single feature: do I keep my records, what does it really cost,
   and is anyone there when it breaks. Hairlines divide the columns; there are
   no icons, because nothing else on this site has one. */
.trust-strip {
  background: var(--surface);
  border-block: 1px solid var(--rule-warm);
  padding-block: 1.7rem;
}
.trust-row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1.6rem;
}
/* The dividers are on the items rather than on the grid, so they disappear
   with the columns at the breakpoint below instead of drawing a stray rule
   down the middle of a stacked list. */
.trust-row > * + * {
  border-left: 1px solid var(--rule-warm);
  padding-left: 1.6rem;
}
.trust-label {
  font-family: var(--font-public-display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: -0.01em;
  color: var(--primary-deep);
  margin-bottom: 0.3rem;
}
.trust-row p:not(.trust-label) {
  color: var(--muted);
  font-size: 0.94rem;
  line-height: 1.55;
}

/* .quote-empty was here and is gone with its markup (2026-09-10). Nothing is
   left behind for a future quote to reuse: a real one is a <blockquote> with a
   name and a town under it, which is a different element with different needs,
   and dead CSS waiting for a shape nobody has designed yet is just dead CSS. */

@media (max-width: 720px) {
  .trust-row { grid-template-columns: minmax(0, 1fr); gap: 1.3rem; }
  .trust-row > * + * {
    border-left: 0;
    padding-left: 0;
    border-top: 1px solid var(--rule-warm);
    padding-top: 1.3rem;
  }
}
/* --- Buttons -------------------------------------------------------------
   Two, and only two. Ink for the action, a ruled link for the alternative.
   No gradient, no pill, no shadow. */
.btn-ink {
  display: inline-block;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-public-display);
  font-weight: 700;
  font-size: 0.95rem;
  padding: 0.8rem 1.5rem;
  border-radius: 2px;
  text-decoration: none;
  transition: background 0.15s;
}
.btn-ink:hover { background: var(--primary-dark); color: var(--paper); }
.btn-ink:focus-visible { outline: 2px solid var(--primary-strong); outline-offset: 3px; }
.btn-quiet {
  display: inline-block;
  font-family: var(--font-public-display);
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-warm);
  padding-bottom: 2px;
}
.btn-quiet:hover { border-bottom-color: var(--primary); color: var(--primary-deep); }
.btn-row { display: flex; align-items: center; gap: 1.4rem; flex-wrap: wrap; }
/* The line under the buttons that carries the price, the trial and the place.
   Specificity, not decoration: it is there so the first screen contains a peso
   figure and a town. */
.btn-meta {
  font-family: var(--font-public-display);
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 1rem;
}

/* --- Ranked rows: what replaced the nine cards --------------------------
   label | body | figure, separated by hairlines, in priority order. The
   figure on the right is a real quantity from the product, not a stat tile. */
.rowlist { border-top: 1px solid var(--ink); }
.rowlist-item {
  display: grid;
  grid-template-columns: 10ch minmax(0, 1fr) 14ch;
  gap: 0.4rem 1.6rem;
  padding: 1.15rem 0;
  border-bottom: 1px solid var(--rule-warm);
  align-items: baseline;
}
.rowlist-item .k {
  font-family: var(--font-figure);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.rowlist-item h3 { font-size: 1.05rem; font-weight: 600; margin-bottom: 0.2rem; }
.rowlist-item p { color: var(--muted); font-size: 0.97rem; }
.rowlist-item .fig {
  font-family: var(--font-figure);
  font-size: 0.78rem;
  color: var(--ink);
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.rowlist-item .fig em {
  font-style: normal; display: block;
  font-family: var(--font-public-display); font-size: 0.72rem; color: var(--muted);
}

/* --- A numbered sequence -------------------------------------------------
   Used for the HMO claim path and the ortho case path, where the numbers
   encode a real order: booking, LOA, invoice, claim. Not used anywhere the
   order is arbitrary. */
.steps { border-top: 1px solid var(--ink); counter-reset: step; }
.steps-item {
  display: grid;
  grid-template-columns: 3ch minmax(0, 1fr);
  gap: 0.2rem 1.4rem;
  padding: 1.05rem 0;
  border-bottom: 1px solid var(--rule-warm);
}
.steps-item::before {
  counter-increment: step;
  content: counter(step);
  font-family: var(--font-figure);
  font-size: 0.85rem;
  color: var(--primary-text);
  padding-top: 0.15rem;
}
.steps-item h3 { font-size: 1rem; font-weight: 600; margin-bottom: 0.2rem; }
.steps-item p { color: var(--muted); font-size: 0.97rem; }

/* --- Notes: prose in columns, ruled on the left. Not cards. ------------- */
.notes { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.2rem 1.8rem; margin-top: 1.6rem; }
.note { border-left: 2px solid var(--primary); padding-left: 1rem; color: var(--muted); font-size: 0.95rem; }
.note strong { color: var(--ink); font-weight: 600; }

/* --- Definition rows: the security list, and anything shaped like it ---- */
.deflist { border-top: 1px solid var(--rule-warm); }
.deflist > div { padding: 1.05rem 0; border-bottom: 1px solid var(--rule-warm); }
.deflist strong {
  display: block;
  font-family: var(--font-public-display);
  font-weight: 600;
  font-size: 0.98rem;
  color: var(--ink);
  margin-bottom: 0.2rem;
}
.deflist p, .deflist > div { color: var(--muted); font-size: 0.95rem; }

/* --- Plain inline list, for the things that are real but not the pitch -- */
.plainlist { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 0.7rem 1.8rem; }
.plainlist p { color: var(--muted); font-size: 0.95rem; }
.plainlist strong { color: var(--ink); font-weight: 600; }

/* --- The ledger: the hero's own figure ---------------------------------
   A real screen from the product (the HMO aging report), drawn in HTML so it
   is crisp, responsive, translatable and weighs nothing -- rather than a
   screenshot. The figures in it are an example and the caption says so. */
.ledger { font-family: var(--font-figure); font-size: 0.8rem; }
.ledger-cap {
  display: flex; justify-content: space-between; gap: 1rem;
  font-size: 0.66rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--muted); padding-bottom: 0.5rem;
}
.ledger table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
/* Column labels, scoped to thead. They were scoped to `th`, which every row's
   own label also is (scope="row", because "Maxicare" heads its row and a
   screen reader should say so before reading the figures). Two things went
   wrong from that: each HMO's name rendered smaller and fainter than its own
   numbers, and the tfoot rule below -- written for `td` -- could not reach the
   total row's first cell, so the 2px ink rule closing the ledger started at
   column two and a stray hairline hung under the word "Outstanding". */
.ledger thead th {
  text-align: right; font-weight: 500; font-size: 0.66rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--muted);
  padding: 0.35rem 0; border-bottom: 1px solid var(--rule-warm);
}
/* A row's label is a name, not a column heading: full size and full ink, so it
   carries at least as much weight as the figures it introduces. Still set in
   caps -- this is an accounting document and the caps are the reason it reads
   as one. */
.ledger tbody th, .ledger tfoot th {
  text-align: left; font-weight: 500; font-size: inherit; letter-spacing: 0.03em;
  text-transform: uppercase; color: var(--ink);
  padding: 0.5rem 0; border-bottom: 1px solid var(--rule-warm);
}
.ledger th:first-child, .ledger td:first-child { text-align: left; }
.ledger td { text-align: right; padding: 0.5rem 0; border-bottom: 1px solid var(--rule-warm); }
.ledger tfoot td, .ledger tfoot th { border-bottom: 0; border-top: 2px solid var(--ink); font-weight: 600; padding-top: 0.55rem; }
.ledger .late { color: var(--danger-text); }
.ledger-note { font-family: var(--font-public-display); font-size: 0.72rem; color: var(--muted); margin-top: 0.6rem; }

@media (max-width: 760px) {
  .rowlist-item { grid-template-columns: minmax(0, 1fr); gap: 0.25rem; }
  .rowlist-item .fig { text-align: left; }
  .rowlist-item .fig em { display: inline; }
  .sec { padding-block: 2.4rem; }
  .site-footer { display: block; }
  .site-footer .footer-links { margin-top: 0.6rem; }
}

/* Two rules the public pages need their own copy of, because they never load
   style.css -- same reason .skip-link is duplicated at the top of this file.

   .table-scroll is the wrapper every table in this project sits in: without it
   a row too wide for a phone pushes the whole PAGE wider than the viewport
   rather than scrolling inside its own box. tests/test_smoke.py holds every
   template to it, which is how the homepage's new aging table was caught.

   .sr-only is for a <caption> that describes a table to a screen reader
   without repeating, on screen, what the visible heading already says. */
/* min-width: 0 alongside overflow-x, deliberately rather than in response to
   a reported bug: a grid or flex item's automatic minimum size is its
   MIN-CONTENT width, so a table inside one can refuse to shrink below the
   widest row it can compose and push the whole page wider than the phone
   screen even though this wrapper says overflow-x: auto. The hero ledger is
   exactly that shape -- a table in a grid track -- and the guard costs
   nothing. Same lesson as the fr-grid rule in style.css ("a grid track can
   shrink too"), one container further out.

   (Checked at 500px, which is as narrow as headless Chrome will actually make
   a window: the homepage stacks, the table fits its column and the page does
   not scroll sideways.) */
.table-scroll { overflow-x: auto; min-width: 0; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* The closing ask, on the homepage and the pricing page. The old one was a
   teal gradient card with "Ready to modernize your practice?" on it -- the
   single most generic sentence on the site, in the single most generic
   container. This is a rule, a sentence and the button. */
.closing-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 1.4rem 2.4rem;
  align-items: end;
  border-top: 1px solid var(--ink);
  padding-top: 1.6rem;
}
.closing-row h2 { font-size: clamp(1.3rem, 3vw, 1.75rem); font-weight: 700; margin-bottom: 0.5rem; }
.closing-row p { color: var(--muted); max-width: 54ch; }

/* Small print that is genuinely small print: the caveats under a price. */
.fine-print { max-width: 68ch; color: var(--muted); font-size: 0.9rem; }

/* A .notes block that sits directly under a .brand-header, where the header's
   own bottom margin has already supplied the space. */
.notes-tight { margin-top: 0; }

@media (max-width: 760px) {
  .closing-row { grid-template-columns: minmax(0, 1fr); }
}

/* ===========================================================================
   THE PUBLIC HALF'S FORM AND COMPONENT VOCABULARY (10 Sep 2026)

   Why these names appear twice in this project. Ten public pages -- /signup,
   /signup/thanks, /trial and the seven /dentists pages -- used to extend
   base.html, the signed-in application's shell, because that is where the
   flash stack, the form controls and the field-error focus behaviour live.
   After the redesign that meant a visitor read the paper-and-serif homepage,
   pressed "Start 30 days free" and landed on something that looked like a
   different company.

   They now extend public_base.html instead, which needs the same vocabulary
   over here: .panel, .form-grid, .btn*, .tag, .empty, .flash*. Same class
   names as style.css, deliberately -- so the templates moved with almost no
   markup changes -- and a different skin, because this is the public side.
   No page ever loads both stylesheets, so the two definitions cannot collide.
   Same precedent as .skip-link, .table-scroll and .sr-only above.

   THE FOUR RULES THAT ARE NOT COSMETIC were ported deliberately and their
   reasons are kept with them: the select needs appearance: none, field errors
   are marked by an attribute selector and not :invalid, the error border has
   to survive focus and greyscale, and [hidden] has to outrank a component's
   own display.
   ======================================================================== */

/* --- Flash messages ------------------------------------------------------
   The public pages had no flash region at all before this, which was fine
   while none of them carried a form. The markup in public_base.html is the
   same as base.html's, including role="alert" on an error so a screen reader
   is told rather than having to reach it in reading order. */
.flash-stack { margin: 0 auto 1.4rem; max-width: 1040px; padding-inline: 1.25rem; display: flex; flex-direction: column; gap: 0.5rem; }
.flash {
  padding: 0.7rem 0.95rem;
  border-radius: 2px;
  border-left: 3px solid var(--rule-warm);
  font-family: var(--font-public-display);
  font-size: 0.9rem;
  font-weight: 500;
}
.flash-success { background: var(--primary-tint); border-left-color: var(--primary-strong); color: var(--primary-dark); }
.flash-error { background: var(--danger-tint); border-left-color: var(--danger); color: var(--danger-text); }
.flash-warning { background: var(--accent-tint); border-left-color: var(--accent); color: var(--accent-text); }

/* --- Page furniture ------------------------------------------------------ */
.page-header { margin-bottom: 1.4rem; }
.page-header h1 { font-size: clamp(1.5rem, 4vw, 2.1rem); font-weight: 700; }
.eyebrow {
  font-family: var(--font-public-display); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); margin-bottom: 0.35rem;
}
/* A panel is a genuine container out here -- a form, a notice, a confirmation.
   One hairline and a 2px radius; no shadow, and no teal fill. */
.panel {
  background: var(--surface);
  border: 1px solid var(--rule-warm);
  border-radius: 2px;
  padding: 1.4rem 1.5rem;
  overflow-x: auto;
}
.panel-form { max-width: 42rem; }
/* The single-panel page: a confirmation, a claim form, a removal request.
   Replaces the app's .booking-screen/.booking-panel pair, which centred a
   card in the viewport and was named for a page these are not. */
.notice-page { max-width: 46rem; margin: 0 auto; padding: 2.6rem 1.25rem 4rem; }
.notice-page h1 { font-size: clamp(1.5rem, 4vw, 2.1rem); font-weight: 700; margin-bottom: 0.6rem; }
.notice-page .lede { font-size: 1.05rem; color: var(--muted); margin-bottom: 1.4rem; }
.notice-links { font-family: var(--font-public-display); font-size: 0.85rem; margin-top: 1.6rem; }
.tag {
  display: inline-block; font-family: var(--font-public-display);
  font-size: 0.72rem; font-weight: 600; letter-spacing: 0.04em;
  padding: 0.2rem 0.5rem; border-radius: 2px;
  background: var(--sunk, var(--surface)); border: 1px solid var(--rule-warm); color: var(--muted);
}
.empty { color: var(--muted); font-size: 0.9rem; }
.help-callout {
  border-left: 2px solid var(--primary); padding: 0.2rem 0 0.2rem 1rem;
  color: var(--muted); font-size: 0.95rem; margin-bottom: 1rem;
}
.help-callout strong { color: var(--ink); font-weight: 600; }
.odontogram-hint { font-size: 0.9rem; color: var(--muted); margin: 0 0 1.2rem; max-width: 66ch; }

/* --- Buttons -------------------------------------------------------------
   .btn-ink and .btn-quiet above are the two the marketing pages use. These
   are the app's names, ported so the moved templates did not have to be
   re-authored: primary is the same ink, secondary is an outline, ghost is a
   plain link-weight control. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-public-display);
  font-size: 0.9rem; font-weight: 600;
  padding: 0.6rem 1.1rem;
  border: 1px solid transparent; border-radius: 2px;
  text-decoration: none; cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.btn-primary { background: var(--ink); color: var(--paper); font-weight: 700; }
.btn-primary:hover { background: var(--primary-dark); color: var(--paper); }
.btn-secondary { background: transparent; border-color: var(--ink); color: var(--ink); }
.btn-secondary:hover { border-color: var(--primary-strong); color: var(--primary-deep); }
.btn-ghost { background: transparent; color: var(--muted); }
.btn-ghost:hover { color: var(--ink); }
.btn-lg { padding: 0.75rem 1.4rem; font-size: 0.95rem; }
.btn:focus-visible { outline: 2px solid var(--primary-strong); outline-offset: 2px; }

/* --- Forms --------------------------------------------------------------- */
.form-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1rem 1.2rem; }
/* display: block, NOT the flex column style.css uses -- and this is a real
   improvement rather than a difference for its own sake. A label here reads
   "Email <span class=label-hint>optional</span>" or "Phone<span class=req>*</span>",
   and under flex-direction: column every one of those spans becomes its own
   flex line: the asterisk and the word "optional" land on a line of their own
   between the label and the field. As a block, the text, the marker and the
   hint sit on one line and the control goes underneath, which is what the
   markup was always describing. */
.form-grid label,
.pform > label {
  display: block;
  font-family: var(--font-public-display);
  font-size: 0.8rem; font-weight: 600; color: var(--muted);
}
.form-grid input, .form-grid select, .form-grid textarea,
.pform input, .pform select, .pform textarea {
  display: block; width: 100%; margin-top: 0.35rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--rule-warm); border-radius: 2px;
  font-family: var(--font-public-prose); font-size: 1rem; line-height: 1.4;
  color: var(--ink); background: var(--surface);
}
/* appearance: none, for the reason style.css records: a native <select> keeps
   its own vertical metrics and renders visibly shorter than the inputs beside
   it even with identical CSS. The arrow is redrawn here, in --muted. */
.form-grid select, .pform select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  padding-right: 2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%235F7369' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round' d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.7rem center; background-size: 13px;
}
.form-grid input:focus, .form-grid select:focus, .form-grid textarea:focus,
.pform input:focus, .pform select:focus, .pform textarea:focus {
  outline: 2px solid var(--primary); outline-offset: 1px; border-color: var(--primary);
}
/* An ATTRIBUTE selector, never :invalid. aria-invalid is set by
   field_error_attrs() in app.py and only after a submission the server
   rejected; :invalid also fires on the browser's own constraint validation,
   which paints every empty required field red before anyone has typed. And
   not colour alone -- the border goes to 2px, so the marking survives
   greyscale and a red-green deficiency (SC 1.4.1). */
.form-grid [aria-invalid="true"],
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
  border: 2px solid var(--danger);
}
/* The focus ring has to stay legible on top of the error border, and the field
   must not appear to fix itself the moment it is focused. */
.form-grid [aria-invalid="true"]:focus,
input[aria-invalid="true"]:focus, select[aria-invalid="true"]:focus,
textarea[aria-invalid="true"]:focus {
  outline: 2px solid var(--danger); outline-offset: 2px; border-color: var(--danger);
}
.checkbox-item {
  display: flex; align-items: flex-start; gap: 0.6rem;
  font-size: 0.95rem; color: var(--ink);
}
.checkbox-item input[type="checkbox"] { margin-top: 0.25rem; accent-color: var(--primary-strong); }
/* Its own rule, and not redundant: the checkbox selector scores higher than
   the attribute selector above, so without this the error border would never
   paint on a consent box -- the one place an unticked box IS the error. */
.checkbox-item input[type="checkbox"][aria-invalid="true"] { outline: 2px solid var(--danger); outline-offset: 2px; }
.span-2 { grid-column: span 2; }
.form-actions { display: flex; justify-content: flex-start; gap: 0.8rem; margin-top: 0.4rem; align-items: center; }
/* A public form: the field grid, then whatever the form has to say before the
   button (a consent line, a note), then the button. One gap value, so the
   rhythm does not depend on which of those a given form happens to have. */
.pform { display: grid; gap: 1.1rem; justify-items: start; }
.pform .form-grid, .pform .directory-consent, .pform p { justify-self: stretch; }
/* The small print inside a label -- "optional", "enquiries go here". Lighter
   than the label it sits in, which is what stops it reading as part of the
   field name. */
.label-hint { color: var(--muted); font-weight: 400; font-size: 0.78rem; }
.req { color: var(--danger-text); margin-left: 0.15rem; }
.required-key { font-size: 0.82rem; color: var(--muted); }
.required-key .req { margin-left: 0; }

/* An author `display` beats the browser's own `[hidden] { display: none }` on
   origin, not specificity -- so `.form-grid label { display: flex }` silently
   defeats `element.hidden = true`. style.css learned this the hard way (a
   field JavaScript "hid" was visible the whole time); this is the same
   general fix, needed here for the same reason. */
[hidden] { display: none !important; }

@media (max-width: 620px) {
  .form-grid { grid-template-columns: minmax(0, 1fr); }
  .span-2 { grid-column: span 1; }
  .form-actions { flex-wrap: wrap; }
}

/* ===========================================================================
   THE PUBLIC CLINIC DIRECTORY (docs/CLINIC_DIRECTORY_DESIGN.md)

   These rules were written a day before the redesign and were the last
   card-and-radius work on the site: bordered tiles with 12px corners on the
   index, a card per listing, a soft-shadow frame around the action panel. They
   moved here from style.css when the pages moved onto public_base.html, and
   they moved into direction A's vocabulary on the way -- a listing is a row in
   a list, and the only object on a listing page that earns a border is the
   thing you act in.

   ONE EXCEPTION, and it is deliberate: .directory-honesty keeps its amber
   fill. Everything else on these pages is quiet, which is exactly why the
   sentence that says "this is not a booking" must not be. It is the one piece
   of copy the whole feature is built around (see directory.py).
   ======================================================================== */

.directory-page { max-width: 1040px; margin: 0 auto; padding: 2.4rem 1.25rem 4rem; }
.directory-page > * { min-width: 0; }
.directory-hero { margin-bottom: 2rem; padding-bottom: 1.6rem; border-bottom: 1px solid var(--rule-warm); }
.directory-hero h1 { font-size: clamp(1.7rem, 4.4vw, 2.4rem); font-weight: 700; margin-bottom: 0.5rem; }
.directory-lede { color: var(--muted); max-width: 62ch; margin: 0 0 1.2rem; }
.directory-search { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 1rem; }
.directory-search input[type="search"] {
  flex: 1 1 16rem; min-width: 0; max-width: 24rem;
  padding: 0.6rem 0.7rem; border: 1px solid var(--rule-warm); border-radius: 2px;
  font-family: var(--font-public-prose); font-size: 1rem; color: var(--ink); background: var(--surface);
}
.directory-search input[type="search"]:focus { outline: 2px solid var(--primary); outline-offset: 1px; border-color: var(--primary); }
.directory-towns { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.directory-towns .tag { text-decoration: none; }
.directory-towns .tag:hover { border-color: var(--primary); color: var(--ink); }
.tag.tag-on { background: var(--primary-tint); border-color: var(--primary-strong); color: var(--primary-dark); }
.directory-count { font-family: var(--font-public-display); color: var(--muted); font-size: 0.85rem; margin: 0 0 0.4rem; }

/* A LIST, not a grid of cards. One rule between neighbours; the clinic's name
   is the only thing that needs to be loud. */
.directory-list { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--ink); }
.directory-card { padding: 1.15rem 0; border-bottom: 1px solid var(--rule-warm); }
.directory-card-name { font-family: var(--font-public-display); font-size: 1.1rem; font-weight: 600; margin: 0 0 0.2rem; }
.directory-card-name a { color: var(--ink); text-decoration: none; }
.directory-card-name a:hover { color: var(--primary-deep); text-decoration: underline; }
.directory-card-meta { color: var(--muted); font-size: 0.92rem; margin: 0 0 0.3rem; }
.directory-card-hours, .directory-card-services { font-size: 0.92rem; color: var(--muted); margin: 0 0 0.25rem; }
.directory-card-actions { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; margin: 0.7rem 0 0; }
.directory-card-note { font-family: var(--font-public-display); color: var(--muted); font-size: 0.78rem; }
.directory-empty { padding: 1.6rem 0; }

.directory-breadcrumb { font-family: var(--font-public-display); font-size: 0.82rem; color: var(--muted); margin: 0 0 0.8rem; }
.directory-breadcrumb a { color: var(--muted); }
.directory-detail-head { padding-bottom: 1.2rem; border-bottom: 1px solid var(--rule-warm); margin-bottom: 1.6rem; }
.directory-detail-head h1 { font-size: clamp(1.7rem, 4.4vw, 2.4rem); font-weight: 700; margin: 0 0 0.3rem; }
.directory-detail-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); gap: 2rem; align-items: start; }
/* Contact details are a list of facts, so they are ruled rather than boxed --
   the action panel beside them is the one object on the page with a border.
   Its own class rather than a .panel that this file then cancels: a class
   applied and immediately undone is how a stylesheet starts lying. */
.directory-contact { min-width: 0; }
.directory-detail-grid h2 { font-size: 1.05rem; font-weight: 600; margin-bottom: 0.8rem; }
.directory-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.5rem 1.1rem; margin: 0; border-top: 1px solid var(--rule-warm); padding-top: 0.7rem; }
.directory-facts dt { font-family: var(--font-public-display); color: var(--muted); font-size: 0.78rem; letter-spacing: 0.04em; text-transform: uppercase; padding-top: 0.15rem; }
.directory-facts dd { margin: 0; font-size: 0.95rem; }
.directory-action-panel { border: 1px solid var(--ink); border-radius: 2px; padding: 1.4rem 1.5rem; background: var(--surface); }

/* THE HONESTY BOX. The loudest thing on a quiet page, on purpose. */
.directory-honesty {
  background: var(--accent-tint); color: var(--accent-text);
  border-left: 3px solid var(--accent); border-radius: 2px;
  padding: 0.8rem 1rem; font-size: 0.93rem; margin: 0 0 1.2rem;
}
.directory-honesty strong { color: var(--accent-text); font-weight: 700; }
.directory-consent { color: var(--muted); font-size: 0.82rem; margin: 1rem 0; }
.directory-facts-note { font-size: 0.92rem; border-left: 2px solid var(--primary); padding: 0.2rem 0 0.2rem 1rem; margin-bottom: 1.2rem; }
.directory-detail-links { font-family: var(--font-public-display); font-size: 0.85rem; margin-top: 1.4rem; }
/* The town chips, grouped under a province label once there is more than one
   province. The group is an inline-flex run rather than a block, so groups
   flow and wrap together instead of each claiming its own line at every width
   -- two provinces of a dozen towns each is two paragraphs of chips, not
   twenty-four rows. */
.directory-town-group {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}
.directory-town-province {
  font-family: var(--font-public-display);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin-left: 0.35rem;
}
.directory-town-group:first-of-type .directory-town-province { margin-left: 0; }

.directory-clinic-note { margin-top: 2.2rem; padding-top: 1.4rem; border-top: 1px solid var(--rule-warm); }
.directory-clinic-note h2 { font-size: 1rem; font-weight: 600; margin: 0 0 0.4rem; }
.directory-clinic-note p { font-size: 0.93rem; color: var(--muted); margin: 0 0 0.5rem; max-width: 76ch; }

/* The claim button and the line that reassures the person about to press it.
   Baseline-aligned rather than stacked so the row reads as one thing; the note
   wraps under the button on a narrow screen. margin-block rather than a bottom
   margin alone, because this sits between two paragraphs and needs air on both
   sides to stop reading as part of either.

   Scoped under .directory-clinic-note deliberately. This is a <p>, and the
   rule above -- `.directory-clinic-note p` -- scores a class plus an element
   type, which beats a bare class; without the parent here its margin and its
   76ch max-width would both win. The first draft of this reached for
   !important twice instead, which is the same mistake in a costume. Same
   lesson as `.form-grid .checkbox-item` in style.css. */
.directory-clinic-note .directory-claim-action {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  margin-block: 0.9rem 1.1rem;
  max-width: none;
}
.directory-clinic-note .directory-claim-action span {
  font-size: 0.86rem;
  color: var(--muted);
}

@media (max-width: 860px) {
  .directory-detail-grid { grid-template-columns: minmax(0, 1fr); gap: 1.6rem; }
}
@media (max-width: 620px) {
  .directory-page { padding: 1.6rem 1rem 3rem; }
}
