:root {
  --color-primary: #2C3E50;   /* legacy Flatly brand-primary: navy, header/footer bg */
  --color-accent: #18BC9C;    /* legacy Flatly brand-success: links, buttons, accents */
  --color-info: #3498DB;
  --color-warning: #F39C12;
  --color-danger: #E74C3C;
  --color-text: #2C3E50;
  --color-bg: #ffffff;
  --color-bg-muted: #ecf0f1;   /* legacy @gray-lighter: jumbotron + footer bg */
  --color-muted: #b4bcc2;      /* legacy @gray-light / @text-muted */
  --color-border: #dfe3e6;
  --font-sans: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-size-base: 15px;
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --radius: 6px;
  --content-width: 1170px;  /* legacy Bootstrap .container width at >=1200px viewports */
}

/* Root font-size: every rem value below (--space-*, heading/form sizes) is
   dimensioned against legacy's 15px @font-size-base. Without this, rem
   resolves against the browser's 16px default and every such value renders
   ~7% too large. */
html { font-size: 15px; }

/* Base */
/* legacy @line-height-base: 20/14 = 1.428571429, not an arbitrary 1.6 */
body { font-family: var(--font-sans); font-size: var(--font-size-base); color: var(--color-text); background: var(--color-bg); line-height: 1.428571429; overflow-x: hidden; }
/* Form controls don't inherit font-family from the page by default (browser
   UA stylesheets set their own system font) - without this they silently
   render in Arial instead of Lato. */
input, button, select, textarea { font-family: inherit; }
/* Links, <p>, headings (+ nested <small>), <hr> and <code> get their
   typography from the vendored legacy Bootstrap build; local copies of
   those rules approximated it and overrode details (e.g. the muted grey of
   `h2 small`). */

/* Legacy's bike-directory page overrides Bootstrap's glyphicon size inline
   (head_additional <style>); it's the same value wherever a glyphicon is used here.
   Except .glyphicon-large, which legacy's own stylesheet sizes at 28px (the
   Advanced Search remove-row icons) - this rule loads later and would win. */
.glyphicon:not(.glyphicon-large) { font-size: 1em; }

/* Look of the DataTables 1.10 Bootstrap-3 integration that legacy wraps its
   admin tables in (search box top-right, sort arrows in the headers, info
   line, button row) - the markup is ours (see admin/fitters_list.html and
   js/admin-fitters.js), only the class names and appearance follow it. */
.dataTables_wrapper { position: relative; clear: both; }
.dataTables_filter { text-align: right; }
.dataTables_filter label { font-weight: normal; white-space: nowrap; text-align: left; }
.dataTables_filter input { margin-left: 0.5em; display: inline-block; width: auto; }
.dataTables_info { padding-top: 8px; white-space: nowrap; clear: both; float: left; }
.dt-buttons { position: relative; float: left; clear: both; padding-top: 8px; }
.dataTables_wrapper::after { content: ""; display: block; clear: both; }
/* sticky = fixedHeader; it also makes each <th> the anchor for its sort arrow */
table#fitter_table thead th { position: sticky; top: 0; background: #fff; z-index: 2; padding-right: 30px; cursor: pointer; border-bottom: 1px solid #2c3e50; }
table#fitter_table thead th.sorting::after,
table#fitter_table thead th.sorting_asc::after,
table#fitter_table thead th.sorting_desc::after {
  position: absolute;
  bottom: 8px;
  right: 8px;
  display: block;
  font-family: 'Glyphicons Halflings';
  opacity: 0.5;
}
table#fitter_table thead th.sorting::after { content: "\e150"; opacity: 0.2; }
table#fitter_table thead th.sorting_asc::after { content: "\e155"; }
table#fitter_table thead th.sorting_desc::after { content: "\e156"; }
table#fitter_table td.dataTables_empty { text-align: center; }

/* Main content width */
main { max-width: var(--content-width); margin: 0 auto;}
/* Pages built from the legacy Bootstrap markup supply their own
   .container/.jumbotron, so <main> must not constrain them. */
main.main-bare { max-width: none; }
main.main-bare > .notice { max-width: var(--content-width); margin-left: auto; margin-right: auto; padding-left: 15px; padding-right: 15px; box-sizing: border-box; }

/* Page-level rules legacy defines inline in its fitters templates. */
#map-canvas { min-height: 400px; }           /* fitter-profile.html / fitters-directory.html */
#map { min-height: 300px; }                   /* fitters-add-edit.html */
#listing-edit-form .form-group label { line-height: 2.6em; margin-bottom: 3px; }

/* Two-column flex rows still used by pages not yet moved onto the legacy
   Bootstrap grid (their children are `.col` / `.col-full`). Scoped with
   :has() so Bootstrap `.row`s (children `.col-md-*` etc.) keep legacy's
   grid. Delete as each page is converted. */
/* Legacy's two-column sections are Bootstrap `.col-md-offset-1 .col-md-5` +
   `.col-md-5` in a 12-column row - not two equal halves. Reproduced as
   percentages of the row (8.3333% = 1/12 offset, 39.1026% = 457.5/1170,
   the actual rendered column width at legacy's own lg container size) so
   it's exact at the desktop widths this was measured against. */
.row:has(> .col, > .col-full) { display: flex; gap: 30px; margin-bottom: var(--space-3); flex-wrap: wrap; margin-left: 0; margin-right: 0; }
.row .col { flex: 0 1 39.1026%; min-width: 260px; }
.row .col:first-child { margin-left: calc(8.3333% + 15px); }
/* Legacy's "Talk to us" section is `.col-md-10.col-md-offset-1`, not a
   full-width column - 80.7692% = 945/1170 (same reference container as
   above), offset by the same 1/12 + gutter formula as the two-col rows. */
.row .col-full { flex: 0 1 80.7692%; margin-left: calc(8.3333% + 15px); }

/* Panels (.panel*), tables (.table*), .text-muted/.text-right/.text-warning
   and .lead come from the vendored legacy Bootstrap build. */

/* Pricing page cards (pricing.html) - legacy's two .well plans are
   text-align: center throughout (pricing.html's `.pricing { text-align:
   center; }`), reproduced here on the existing .panel/.panel-info shell
   rather than introducing a separate well style. Matches legacy's
   `.pricing hr { border-color: #d7d7d7; }` for the hr-separated bullet
   stack inside each card too. */
.pricing-panel { margin-top: 2em; }
.pricing-panel .panel-body { text-align: center; }
.pricing-panel .panel-body hr { border-color: #d7d7d7; }

/* Ad slot placeholder: shown where GPT_ADS_ENABLED is off (components/gpt_ad.html) */
/* Placeholder sized like legacy's GPT leaderboard slot (see its
   googletag.sizeMapping in public/index.html): 320x50 below 500px, 468x60
   from 500px, 728x90 from 768px, 970x90 from 992px. */
.ad-slot {
  width: 320px;
  height: 50px;
  max-width: 100%;
  margin: 0 auto;
  text-align: center;
  overflow: hidden;
}
@media (min-width: 500px) { .ad-slot { width: 468px; height: 60px; } }
@media (min-width: 768px) { .ad-slot { width: 728px; height: 90px; } }
@media (min-width: 992px) { .ad-slot { width: 970px; } }
/* Bike page: its GPT mapping (public/bike.html) uses a 336x280 rectangle from
   992px up instead of the 970x90 leaderboard. */
@media (min-width: 992px) { .ad-slot.ad-slot-rect { width: 336px; height: 280px; } }
.ad-slot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  border-radius: var(--radius);
}

/* reCAPTCHA badge: static placeholder, real widget needs a site key */
.recaptcha-badge {
  position: fixed;
  bottom: 1rem;
  right: 1rem;
  padding: 0.5rem 0.75rem;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
  color: var(--color-muted);
  font-size: 0.7rem;
  z-index: 1000;
}

/* Login / account */
.login-page { max-width: 420px; margin: 0 auto; text-align: center; }

/* (The account page's own layout rules are gone: it uses the legacy Bootstrap
   grid now.) */
.text-danger { color: var(--color-danger); }



/* Add a bike */

/* legacy .media/.media-left/.media-body (style.css:5167) - plain stacked
   option rows, no card/border chrome (unlike this app's previous
   bordered-box treatment). */
.add-bike-method-form .media { display: flex; margin-top: 15px; }
.add-bike-method-form .media:first-child { margin-top: 0; }
.add-bike-method-form .media-left { padding-right: 10px; }
.add-bike-method-form .media-body p { margin: 0; }

/* legacy's custom oversized circular radio button (style.css:7388-7453,
   "Based on https://github.com/Chalarangelo/bootstrap-extend") - an
   invisible native radio layered under a label whose ::before draws the
   actual 24px circle, teal when checked. */
input[type="radio"].rdbtn { opacity: 0; position: absolute; margin: 5px; cursor: pointer; }
input[type="radio"].rdbtn + label { position: relative; display: inline-block; vertical-align: middle; margin: 5px; cursor: pointer; }
input[type="radio"].rdbtn + label::before {
  content: "";
  display: inline-block;
  vertical-align: middle;
  width: 24px;
  height: 24px;
  margin-right: 10px;
  border-radius: 50%;
  border: 1px solid #15a589;
  background: #fff;
}
input[type="radio"].rdbtn:checked + label::before { background: var(--color-accent); box-shadow: inset 0 0 0 3px #fff; }
input[type="radio"].rdbtn:hover + label::before, input[type="radio"].rdbtn:focus + label::before { box-shadow: inset 0 0 0 3px #fff; background: var(--color-accent); }

.add-bike-method-form { margin-top: var(--space-2); }

.add-bike-form { max-width: 500px; margin-top: var(--space-2); }

/* legacy's Geometry-entry cell inputs (.table-inputs, style.css:7180):
   thin/square-cornered/center-aligned, distinct from the rounded
   .form-control used elsewhere on the same page. */
.table-inputs { border-width: 0.5px; border-radius: 0; text-align: center; padding: 10px 3px; height: auto; }
.geometry-table-wrap { overflow-x: auto; }
#multi_geometry_table { border-collapse: collapse; }
#multi_geometry_table th, #multi_geometry_table td { padding: 4px; vertical-align: top; }
#multi_geometry_table .param-label { white-space: nowrap; padding-right: 15px; text-align: left; }
.add-size-control { text-align: right; }

/* Geometry-import column-mapping precheck page
   (geometry_import_precheck.html) - the "drunk matching" confirmation
   table, plain CSS equivalent of legacy's select2/Bootstrap markup for
   the same page. */
.precheck-mapping-table { width: 100%; border-collapse: collapse; margin-top: var(--space-2); }
.precheck-mapping-table th, .precheck-mapping-table td { padding: 10px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--color-border); }
.precheck-icon-col { width: 32px; text-align: center; }
.precheck-icon { display: inline-block; width: 22px; height: 22px; line-height: 22px; border-radius: 50%; text-align: center; font-weight: 700; font-size: 0.85rem; }
.precheck-icon-ok { background: #dff0d8; color: #3c763d; }
.precheck-icon-warning { background: #fcf8e3; color: #8a6d3b; }
.precheck-icon-guess { background: var(--color-bg-muted); color: #597ea2; }
.precheck-ok { color: #3c763d; }
.precheck-warning { color: #8a6d3b; }
.precheck-guess { color: #597ea2; }

/* CSV-import review page (geometry_import_review.html) live-validation
   states, ported from legacy's has-warning/has-error classes. */
.has-warning .form-control { background: #fdf5e7; }
.has-warning .form-control:focus { background: inherit; }
.has-error .form-control { background: #fbeaea; }
.has-error .form-control:focus { background: inherit; }

.advanced-search-screenshot { max-width: 100%; height: auto; margin: var(--space-2) 0; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); }

.advanced-search-hint { margin-top: var(--space-2); }
.advanced-search-help { margin: var(--space-2) 0; padding: var(--space-2); border: 1px solid var(--color-border); border-radius: var(--radius); background: var(--color-bg-muted); }
.advanced-search-help summary { cursor: pointer; font-weight: 700; }
.advanced-search-help ul { margin: var(--space-1) 0 0; padding-left: 1.2rem; }
.advanced-search-help li { margin-bottom: 0.4rem; }

.advanced-search-form { max-width: 700px; margin-top: var(--space-2); }
.parameter-row { display: flex; gap: 0.5rem; align-items: center; margin-bottom: var(--space-1); }
.parameter-row .field-select { flex: 0 0 200px; }
.parameter-row .value-slot { flex: 1 1 auto; display: flex; }
.parameter-row .value-slot input, .parameter-row .value-slot select { width: 100%; }
.advanced-search-form input, .advanced-search-form select { padding: 0.5rem 0.75rem; border: 1px solid var(--color-border); border-radius: var(--radius); font-family: inherit; font-size: 1rem; }
.parameter-row .remove-row { flex: 0 0 auto; background: none; border: 1px solid var(--color-border); border-radius: var(--radius); width: 2.2rem; height: 2.2rem; line-height: 1; cursor: pointer; color: var(--color-muted); }
.parameter-row .remove-row:hover { background: var(--color-bg-muted); }
.add-row-button { display: inline-block; margin-top: 0.3rem; background: none; border: 1px dashed var(--color-border); border-radius: var(--radius); padding: 0.5rem 1rem; cursor: pointer; color: var(--color-text); }
.add-row-button:hover { border-color: var(--color-accent); }
.advanced-search-form .form-actions { margin-top: var(--space-2); }
.advanced-search-form button[type="submit"] { padding: 0.6rem 1.5rem; background: var(--color-accent); color: #fff; border: none; border-radius: var(--radius); cursor: pointer; }
.advanced-search-form button[type="submit"]:hover { background: #14a085; }

/* Bootstrap 3 primitives (.breadcrumb, .form-group, .form-control, .checkbox,
   .btn*, .label*, .help-block) come from the vendored legacy build. Local
   copies were removed because, loading after it, they overrode legacy's
   modifier classes (e.g. .input-lg). */
.help-text { display: block; margin-top: 5px; margin-bottom: 10px; color: #597ea2; font-size: 0.9rem; }

/* Bike Fitters directory - legacy's two equal Bootstrap columns
   (`.col-sm-6` + `.col-sm-6`, a true 50/50 split - not the offset pattern
   `.row .col` above, which is legacy's *different* 1/12-offset two-col
   layout used elsewhere e.g. the homepage). */
.row.row-halves { display: flex; gap: 30px; flex-wrap: wrap; }
.row.row-halves > .col-half { flex: 1 1 calc(50% - 15px); min-width: 260px; }
address { margin-bottom: 21px; font-style: normal; line-height: 1.428571429; }
.fitter-map { min-height: 300px; height: 300px; border-radius: var(--radius); border: 1px solid var(--color-border); }

@media (max-width: 720px) {
  .row.row-halves { flex-direction: column; gap: var(--space-2); }
  .row.row-halves > .col-half { flex-basis: auto; }
}

/* Stub notice */
.stub-notice { padding: var(--space-4) var(--space-2); text-align: center; }
.stub-notice h1 { color: var(--color-warning); }


/* Notices. (Flashed messages are Bootstrap alerts now - see base.html.) */
.notice { background: #fff8e1; border: 1px solid #e0c26b; padding: 0.75rem 1rem; border-radius: var(--radius); margin-bottom: var(--space-3); }

/* Visually-hidden-but-accessible utility (standard Bootstrap-style
   .sr-only pattern) - referenced by users_list.html's icon-only column
   headers and search label but was never actually defined, so that text
   was rendering visibly instead of being screen-reader-only. */
.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;
}

/* Admin users list (/admin/users/) - reproduces legacy's actual layout
   (users-listall.html): breadcrumb/heading/search box sit in a narrower
   centered column (legacy's .col-lg-10.col-lg-offset-1, ~83% width, here
   .user-table-narrow), the table itself gets the *full* centered page
   width (legacy's .col-md-12 in its own row, wider than the search box
   above it) - both centered within the page via .user-table-page, not
   left-aligned/narrowed the way the offset .row/.col-full grid used
   elsewhere in this app would (that grid is wrong here: a table that needs
   to scroll/expand should re-center itself, not get pushed left by a
   margin offset). All rows render at once (no vertical clipping/inner
   scroll box - .user-table-scroll only adds horizontal scroll for narrow
   viewports, since the table has a min-width); sortable/icon column
   headers and a position:sticky <thead> stick to the *page's* scroll (not
   a boxed one) for the on-scroll fixed-header behaviour, matching
   legacy's FixedHeader plugin. This, plus the search/sort/pagination JS
   (app/static/js/admin-users.js), are vanilla CSS/JS rather than
   DataTables/jQuery/FixedHeader, per this app's no-jQuery/no-framework
   convention - a deliberate exception to matching legacy's exact JS
   stack, not the visual layout (see MEMORY.md's admin-table-pattern note).
*/
.user-table-page { max-width: var(--content-width); margin: 0 auto; }
.user-table-narrow { max-width: 83.3333%; margin: 0 auto; }
.user-table-search { margin: 0 0 var(--space-2); }
.user-table-scroll { overflow-x: auto; }
.user-table { min-width: 900px; width: 100%; margin: 0 auto; }
.user-table thead th {
  position: sticky;
  top: 0;
  background: var(--color-bg-muted);
  border-bottom: 2px solid var(--color-border);
  padding: 8px 5px;
  text-align: left;
  white-space: nowrap;
  z-index: 1;
}
.user-table th[data-sort] { cursor: pointer; user-select: none; }
.user-table th[data-sort]:hover { color: var(--color-accent); }
.user-table th[data-sort]::after { content: ""; display: inline-block; width: 0.7em; margin-left: 0.2em; color: var(--color-muted); }
.user-table th.sorted-asc::after { content: "▲"; color: var(--color-accent); }
.user-table th.sorted-desc::after { content: "▼"; color: var(--color-accent); }
.user-table td.text-center, .user-table th.text-center { text-align: center; }
.user-table tbody[aria-busy="true"] { opacity: 0.5; }
.glyph-ok { color: var(--color-accent); font-weight: 700; }

.user-table-pagination { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-2); }
.pagination-controls { display: flex; flex-wrap: wrap; gap: 0.3rem; }
.pagination-btn {
  padding: 0.3rem 0.65rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: #fff;
  color: var(--color-text);
  cursor: pointer;
  font-family: inherit;
}
.pagination-btn:hover:not(:disabled) { border-color: var(--color-accent); color: var(--color-accent); }
.pagination-btn.active { background: var(--color-accent); border-color: var(--color-accent); color: #fff; cursor: default; }
.pagination-btn:disabled { cursor: default; }

@media (max-width: 720px) {
  .row { flex-direction: column; gap: var(--space-2); }
  .row .col, .row .col-full { flex-basis: auto; }
  .row .col:first-child, .row .col-full { margin-left: 0; }
}
