/* dour.store — the button system.
 *
 * Section 22 of the upgrade layer, in its own file. Loaded straight after
 * `upgrade.css`, carrying the same `html[data-ui="upgrade"]` scope and the
 * same contract: nothing here replaces a rule in `dour.css`, and dropping the
 * attribute gives back the origin byte for byte — which is what
 * `tools/compare-live.mjs` measures.
 *
 * It is a separate file for one reason only: `upgrade.css` was being written
 * by another session while this was authored (its section 16 grew 93 lines at
 * 12:47 on 2026-09-10). Two whole-file writes over one file is how one of
 * them disappears. Nothing here depends on being separate — paste it onto the
 * end of `upgrade.css` as section 22 whenever that file is quiet.
 *
 * WHY THE BUTTONS NEEDED A SYSTEM. Three things, each measured on the running
 * rebuild rather than read off the markup:
 *
 *  1. `dour.css` cancels the hover on every button on the site. The compiled
 *     stylesheet defines `.btn:hover` twice — Bootstrap's own at byte 100296,
 *     then Odoo's at 100435, which sets colour, background and border back to
 *     the *resting* values. The theme therefore ships buttons that do not
 *     answer a pointer at all, and the upgrade layer had been buying the
 *     answer back one selector at a time with `!important`. Section 22.2
 *     restores it once, for every variant.
 *
 *  2. `.btn-secondary` is white on white. The theme resolves it to a
 *     `rgba(255, 255, 255, 0.85)` fill inside a `rgba(255, 255, 255, 0.85)`
 *     border, and the storefront spends it on two real actions: **Apply**,
 *     beside the discount field on the cart, and **Discard**, beside Continue
 *     checkout on the address step. Both render as bare text on a white card.
 *     That is not a low-contrast button; there is no button.
 *     `.btn-outline-secondary` is the same colour and the same story.
 *     Sections 22.5 and 22.7.
 *
 *  3. Nine variants, nine geometries. The same `.btn` came back 38x44, 44x46,
 *     45x40, 54x44, 63x22, 89x44, 92x48 and 300x48 across the routes, with
 *     corner radii of 0, 4.8, 6.4, 20, 100 and 999px, in three weights and
 *     four sizes. Sections 22.1 and 22.9 give them one shell and one ladder.
 *
 * THE MECHANISM. Odoo 18 ships Bootstrap 5.3, whose buttons are driven
 * entirely by `--btn-*` custom properties — `.btn-light` is fifteen of them
 * and nothing else. Setting those properties instead of the painted result
 * means every state a variant owns (hover, active, disabled, focus) keeps
 * coming from one place and stays consistent by construction, and it needs no
 * `!important` to beat the origin: `html[data-ui="upgrade"] .btn-light` is
 * simply more specific than `.btn-light`.
 *
 * THE LOOK. The theme's own pill, ink and navy. The origin already sets
 * `--btn-border-radius: 1.25rem` and the layer's display type is uppercase and
 * tracked, so the *actioning* variants follow the headings — uppercase, 600,
 * 0.1em — while the quiet ones (link, light, close, the icon circles) stay in
 * sentence case, so a page keeps two registers instead of shouting in one. No
 * new hues: every colour below is `--o-color-1`, `--o-color-5`, or a grey the
 * theme already defines, mixed with paper.
 * ================================================================== */

/* ==================================================================
 * 22.0 · Tokens
 *
 * `--ub-*` is this file's namespace, so nothing here can be picked up by a
 * rule elsewhere by accident. Heights are a four-step ladder around the 44px
 * practical touch floor; the casts are navy at low alpha rather than black,
 * because a black shadow under a navy button greys it.
 * ================================================================== */
html[data-ui="upgrade"] {
  --ub-h-sm: 36px;
  --ub-h: 46px;
  --ub-h-lg: 52px;
  --ub-h-xl: 58px;

  --ub-px-sm: 16px;
  --ub-px: 26px;
  --ub-px-lg: 34px;
  --ub-px-xl: 40px;

  --ub-radius: 999px;

  /* Hairlines and washes, all from the theme's ink and navy over paper. */
  --ub-hair: color-mix(in srgb, var(--u-ink, #292121) 15%, transparent);
  --ub-hair-strong: color-mix(in srgb, var(--u-ink, #292121) 34%, transparent);
  --ub-surface: color-mix(in srgb, var(--u-ink, #292121) 4%, var(--u-paper, #fff));
  --ub-surface-hi: color-mix(in srgb, var(--u-navy, #00203f) 7%, var(--u-paper, #fff));
  --ub-surface-press: color-mix(in srgb, var(--u-navy, #00203f) 13%, var(--u-paper, #fff));
  --ub-navy-hair: color-mix(in srgb, var(--u-navy, #00203f) 30%, transparent);

  /* Depth. One cast at rest, one on hover, and a white top-light for fills. */
  --ub-cast: 0 8px 18px -12px color-mix(in srgb, var(--u-navy, #00203f) 70%, transparent);
  --ub-cast-hi: 0 18px 34px -14px color-mix(in srgb, var(--u-navy, #00203f) 78%, transparent);
  --ub-sheen: linear-gradient(180deg, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 58%);
  --ub-ring: 0 0 0 3px color-mix(in srgb, var(--u-navy, #00203f) 20%, transparent);

  --ub-t: 180ms;
  --ub-t-slow: 280ms;
}

/* ==================================================================
 * 22.1 · The shell every button shares
 *
 * One geometry, one type, one transition list. Everything is set through the
 * `--btn-*` properties Bootstrap already reads, except the four things it has
 * no property for: the flex centring, the minimum height, the tracking, and
 * the transition.
 * ================================================================== */
html[data-ui="upgrade"] .btn {
  --btn-font-family: var(--font-sans-serif);
  --btn-font-size: 0.8125rem;
  --btn-font-weight: 600;
  --btn-line-height: 1.1;
  --btn-border-width: 1px;
  --btn-border-radius: var(--ub-radius);
  --btn-padding-x: var(--ub-px);
  --btn-padding-y: 0;
  --btn-box-shadow: none;
  --btn-disabled-opacity: 0.42;

  min-height: var(--ub-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  letter-spacing: 0.045em;
  text-decoration: none;
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
  transition:
    color var(--ub-t) var(--u-ease),
    background-color var(--ub-t) var(--u-ease),
    border-color var(--ub-t) var(--u-ease),
    box-shadow var(--ub-t-slow) var(--u-out),
    translate var(--ub-t-slow) var(--u-out),
    scale var(--ub-t-slow) var(--u-out);
}

/* The actioning variants carry the display voice; the quiet ones do not.
 * Splitting them here is what keeps a page from reading as one long shout. */
html[data-ui="upgrade"]
  :is(.btn-primary, .btn-secondary, .btn-outline-primary, .btn-outline-secondary) {
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

/* Icon-only buttons are targets, not pills with a gap in them. */
html[data-ui="upgrade"] .btn:has(> .fa:only-child),
html[data-ui="upgrade"] .btn:has(> i:only-child) {
  gap: 0;
}

/* ==================================================================
 * 22.2 · Hover, restored
 *
 * The one rule this file exists for. `dour.css`, in order:
 *
 *   .btn:hover { color: var(--btn-hover-color); ... }   <- Bootstrap
 *   .btn:hover { color: var(--btn-color); ... }         <- Odoo, and it wins
 *
 * Reinstating Bootstrap's intent at a specificity the theme cannot reach puts
 * a hover back on every variant at once, including the ones no section of the
 * upgrade layer had reached individually: light, secondary, both outlines,
 * close, and the search submit. The fallbacks matter — a bare `.btn` has no
 * `--btn-hover-bg`, and must resolve to its own resting fill rather than to
 * nothing.
 * ================================================================== */
html[data-ui="upgrade"] .btn:hover {
  color: var(--btn-hover-color, var(--btn-color));
  background-color: var(--btn-hover-bg, var(--btn-bg));
  border-color: var(--btn-hover-border-color, var(--btn-border-color));
}

/* ==================================================================
 * 22.3 · Press
 *
 * A control that lifts on approach and gives under the finger reads as a
 * physical thing. `translate` rather than `transform`, because section 10
 * owns `transform` on anything the scroll animates, and an animation on that
 * property would win over the hover for as long as the page is open.
 * ================================================================== */
html[data-ui="upgrade"] .btn:hover {
  translate: 0 -1px;
}

html[data-ui="upgrade"] .btn:active,
html[data-ui="upgrade"] .btn:first-child:active {
  translate: 0 1px;
  scale: 0.985;
  transition-duration: 60ms;
}

/* ==================================================================
 * 22.4 · Primary — the one thing the page is asking for
 *
 * Navy fill, deeper navy on hover, and a navy-tinted cast so the button sits
 * above the page rather than in it. The sheen is a 12% white top-light on the
 * fill: it gives the pill a horizon without introducing a colour.
 * ================================================================== */
html[data-ui="upgrade"] .btn-primary {
  --btn-color: var(--u-paper, #fff);
  --btn-bg: var(--u-navy, #00203f);
  --btn-border-color: var(--u-navy, #00203f);
  --btn-hover-color: var(--u-paper, #fff);
  --btn-hover-bg: var(--u-deep, #14212e);
  --btn-hover-border-color: var(--u-deep, #14212e);
  --btn-active-color: var(--u-paper, #fff);
  --btn-active-bg: var(--u-deep, #14212e);
  --btn-active-border-color: var(--u-deep, #14212e);
  --btn-active-shadow: none;
  --btn-disabled-color: var(--u-paper, #fff);
  --btn-disabled-bg: var(--u-navy, #00203f);
  --btn-disabled-border-color: var(--u-navy, #00203f);

  min-height: var(--ub-h-lg);
  background-image: var(--ub-sheen);
  box-shadow: var(--ub-cast);
}

html[data-ui="upgrade"] .btn-primary:hover {
  box-shadow: var(--ub-cast-hi);
}

html[data-ui="upgrade"] .btn-primary:active,
html[data-ui="upgrade"] .btn-primary:first-child:active {
  box-shadow: var(--ub-cast);
}

/* The skip link is `rounded-0` on purpose and is only ever seen on focus; a
 * cast under it would be the first thing a keyboard visitor met. */
html[data-ui="upgrade"] .btn.rounded-0 {
  box-shadow: none;
  background-image: none;
}

/* ==================================================================
 * 22.5 · Secondary — the invisible button
 *
 * Was: `rgba(255, 255, 255, 0.85)` on `rgba(255, 255, 255, 0.85)`, on a white
 * card, for Apply on the cart and Discard on the address step. Now the paired
 * answer to primary: an ink hairline that fills navy when it is reached for,
 * so the two read as one decision with two answers rather than as a button
 * and a stray word beside it.
 * ================================================================== */
html[data-ui="upgrade"] .btn-secondary {
  --btn-color: var(--u-ink, #292121);
  --btn-bg: transparent;
  --btn-border-color: var(--ub-hair-strong);
  --btn-hover-color: var(--u-paper, #fff);
  --btn-hover-bg: var(--u-navy, #00203f);
  --btn-hover-border-color: var(--u-navy, #00203f);
  --btn-active-color: var(--u-paper, #fff);
  --btn-active-bg: var(--u-deep, #14212e);
  --btn-active-border-color: var(--u-deep, #14212e);
  --btn-active-shadow: none;
  --btn-disabled-color: var(--u-ink-3, #7d6c6c);
  --btn-disabled-bg: transparent;
  --btn-disabled-border-color: var(--ub-hair);

  min-height: var(--ub-h-lg);
}

html[data-ui="upgrade"] .btn-secondary:hover {
  box-shadow: var(--ub-cast);
}

/* ==================================================================
 * 22.6 · Light — the utility surface
 *
 * 1,348 of these in the captured markup, most of them the wishlist heart on a
 * grid card. The theme gives it `#f0f3f5` with a `#f2f4f7` hover: a two-unit
 * change, which is no change. A real surface, a real edge, and a hover that
 * tints towards the brand instead of towards nothing.
 *
 * `.products_header .btn-light` (section 16) sets its own geometry with
 * `!important` and keeps it; only the colours below reach it.
 * ================================================================== */
html[data-ui="upgrade"] .btn-light {
  --btn-color: var(--u-ink, #292121);
  --btn-bg: var(--ub-surface);
  --btn-border-color: var(--ub-hair);
  --btn-hover-color: var(--u-navy, #00203f);
  --btn-hover-bg: var(--ub-surface-hi);
  --btn-hover-border-color: var(--ub-navy-hair);
  --btn-active-color: var(--u-navy, #00203f);
  --btn-active-bg: var(--ub-surface-press);
  --btn-active-border-color: var(--ub-navy-hair);
  --btn-active-shadow: none;
  --btn-disabled-color: var(--u-ink-3, #7d6c6c);
  --btn-disabled-bg: var(--ub-surface);
  --btn-disabled-border-color: var(--ub-hair);

  --btn-font-weight: 500;
}

/* ==================================================================
 * 22.7 · Outline
 *
 * `.btn-outline-secondary` was the white-on-white problem again, this time as
 * a border. Both outlines are now the same ghost at two weights of ink, and
 * both fill on hover, which is the behaviour the class name promises and the
 * theme's cancelled hover never delivered.
 * ================================================================== */
html[data-ui="upgrade"] .btn-outline-primary {
  --btn-color: var(--u-navy, #00203f);
  --btn-bg: transparent;
  --btn-border-color: var(--ub-navy-hair);
  --btn-hover-color: var(--u-paper, #fff);
  --btn-hover-bg: var(--u-navy, #00203f);
  --btn-hover-border-color: var(--u-navy, #00203f);
  --btn-active-color: var(--u-paper, #fff);
  --btn-active-bg: var(--u-deep, #14212e);
  --btn-active-border-color: var(--u-deep, #14212e);
  --btn-active-shadow: none;
  --btn-disabled-color: var(--u-ink-3, #7d6c6c);
  --btn-disabled-bg: transparent;
  --btn-disabled-border-color: var(--ub-hair);
}

html[data-ui="upgrade"] .btn-outline-secondary {
  --btn-color: var(--u-ink-2, #574949);
  --btn-bg: transparent;
  --btn-border-color: var(--ub-hair-strong);
  --btn-hover-color: var(--u-paper, #fff);
  --btn-hover-bg: var(--u-ink, #292121);
  --btn-hover-border-color: var(--u-ink, #292121);
  --btn-active-color: var(--u-paper, #fff);
  --btn-active-bg: var(--u-ink, #292121);
  --btn-active-border-color: var(--u-ink, #292121);
  --btn-active-shadow: none;
  --btn-disabled-color: var(--u-ink-3, #7d6c6c);
  --btn-disabled-bg: transparent;
  --btn-disabled-border-color: var(--ub-hair);
}

html[data-ui="upgrade"] .btn-outline-primary:hover,
html[data-ui="upgrade"] .btn-outline-secondary:hover {
  box-shadow: var(--ub-cast);
}

/* ==================================================================
 * 22.8 · Link — the quiet register
 *
 * A pill shell around a word is wrong, so `.btn-link` opts out of the
 * geometry and takes an underline instead: drawn as a background gradient so
 * it can grow from nothing, which `text-decoration` cannot do, and so it
 * never collides with the `text-decoration: none !important` the product page
 * sets on its own wishlist link.
 *
 * The two `.btn-link`s that are not links — the quantity steppers and the
 * icon-led toggles — are excluded by having an icon as their only child.
 * Section 22.12 gives the steppers their own shape.
 * ================================================================== */
html[data-ui="upgrade"] .btn-link {
  --btn-color: var(--u-ink, #292121);
  --btn-bg: transparent;
  --btn-border-color: transparent;
  --btn-hover-color: var(--u-navy, #00203f);
  --btn-hover-bg: transparent;
  --btn-hover-border-color: transparent;
  --btn-active-color: var(--u-navy, #00203f);
  --btn-active-bg: transparent;
  --btn-active-border-color: transparent;
  --btn-padding-x: 0;
  --btn-padding-y: 4px;
  --btn-font-weight: 500;
  --btn-border-radius: 4px;

  /* Not the pill's 46px, but not nothing either. The shell's zero vertical
   * padding took the search facets down to 15px tall, under WCAG 2.2 AA's
   * 24px floor for a target (2.5.8); 24px is the floor, and the padding is
   * what a word needs to be pointed at rather than aimed at. The shell's
   * `nowrap` also goes: a pill should not break, but "Men's Accessories" in a
   * results row should. */
  min-height: 24px;
  letter-spacing: 0.01em;
  text-transform: none;
  white-space: normal;
}

html[data-ui="upgrade"] .btn-link:hover,
html[data-ui="upgrade"] .btn-link:active {
  translate: none;
  scale: none;
}

html[data-ui="upgrade"] .btn-link:not(:has(> .fa:only-child)):not(:has(> i:only-child)):not(.js_add_cart_json) {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition:
    color var(--ub-t) var(--u-ease),
    background-size var(--ub-t-slow) var(--u-out);
}

html[data-ui="upgrade"] .btn-link:not(:has(> .fa:only-child)):not(:has(> i:only-child)):not(.js_add_cart_json):hover {
  background-size: 100% 1px;
}

/* ==================================================================
 * 22.9 · The size ladder
 *
 * Four steps, and `.btn-sm` keeps its right to be small: the search results
 * use `.btn-link.btn-sm` as a 22px facet chip, and forcing 36px on it would
 * push every result row apart to make room for one word.
 * ================================================================== */
html[data-ui="upgrade"] .btn-sm {
  --btn-font-size: 0.75rem;
  --btn-padding-x: var(--ub-px-sm);
  min-height: var(--ub-h-sm);
}

html[data-ui="upgrade"] .btn-lg {
  --btn-font-size: 0.875rem;
  --btn-padding-x: var(--ub-px-lg);
  min-height: var(--ub-h-lg);
}

html[data-ui="upgrade"] .btn-xl {
  --btn-font-size: 0.9375rem;
  --btn-padding-x: var(--ub-px-xl);
  min-height: var(--ub-h-xl);
}

html[data-ui="upgrade"] .btn-link.btn-sm,
html[data-ui="upgrade"] .btn-link.btn-lg {
  min-height: 24px;
}

/* ==================================================================
 * 22.10 · Segmented groups
 *
 * The one on the site is the size guide's IN / CM toggle, cloned again into
 * the modal `UpgradeLayer` builds. Bootstrap's group is two half-pills
 * sharing a seam, and its segments inherit whatever width their word happens
 * to need — IN came back 38px wide beside CM at 46px. This is the tray form:
 * one shell at the group's radius, equal segments inside it, and the chosen
 * one carried on a navy pill.
 * ================================================================== */
html[data-ui="upgrade"] .btn-group {
  display: inline-flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--ub-hair);
  border-radius: var(--ub-radius);
  background: var(--ub-surface);
}

html[data-ui="upgrade"] .btn-group > .btn {
  --btn-border-radius: var(--ub-radius);
  --btn-bg: transparent;
  --btn-border-color: transparent;
  --btn-color: var(--u-ink-2, #574949);
  --btn-hover-bg: var(--u-paper, #fff);
  --btn-hover-border-color: transparent;
  --btn-hover-color: var(--u-navy, #00203f);

  flex: 1 1 0;
  min-width: 62px;
  min-height: var(--ub-h-sm);
  margin: 0 !important;
  border-radius: var(--ub-radius) !important;
  box-shadow: none;
  background-image: none;
}

html[data-ui="upgrade"] .btn-group > .btn:hover {
  translate: none;
  box-shadow: none;
}

html[data-ui="upgrade"] .btn-group > .btn.active,
html[data-ui="upgrade"] .btn-group > .btn-check:checked + .btn {
  background-color: var(--u-navy, #00203f) !important;
  border-color: var(--u-navy, #00203f) !important;
  color: var(--u-paper, #fff) !important;
  box-shadow: var(--ub-cast);
}

html[data-ui="upgrade"] .btn-group > .btn:active,
html[data-ui="upgrade"] .btn-group > .btn:first-child:active {
  translate: none;
  scale: 0.97;
}

/* ==================================================================
 * 22.11 · The header's icon buttons
 *
 * Three targets that do the same kind of thing and looked like three kinds of
 * thing: cart and search came back as 40x40 circles on `rgba(0, 0, 0, 0.05)`,
 * and the account icon as a `#f0f3f5` circle with a 1.6px edge nested inside
 * a 42x42 wrapper with a 20px radius. `bg-o-color-3` is a utility and carries
 * `!important`, which is the one place in this file that has to be answered
 * in kind.
 *
 * These are keyed on `.o_main_nav`, not `#o_main_nav`. The captured chrome
 * ships the bar twice — the desktop copy carries the id, and the phone copy,
 * `d-lg-none`, carries only the class. Section 14 equalised the boxes on the
 * id, so on a phone none of it applied and the cart target was 36x46 with no
 * ground at all (measured at 375px). The class matches both bars, so the
 * geometry is restated here rather than left to a selector that reaches one
 * of them.
 * ================================================================== */
html[data-ui="upgrade"] .o_main_nav .navbar-nav.align-items-center > li > a.btn.rounded-circle,
html[data-ui="upgrade"] .o_main_nav .o_navlink_background_hover > div.btn.rounded-circle {
  width: 40px;
  height: 40px;
  min-height: 40px;
  padding: 0 !important;
  border-radius: 50%;
  background-color: color-mix(in srgb, var(--u-ink, #292121) 6%, transparent) !important;
  background-image: none;
  border: 1px solid transparent !important;
  box-shadow: none;
  color: var(--u-ink, #292121);
}

html[data-ui="upgrade"] .o_main_nav .navbar-nav.align-items-center > li > a.btn.rounded-circle:hover,
html[data-ui="upgrade"] .o_main_nav .o_navlink_background_hover:hover > div.btn.rounded-circle {
  background-color: var(--u-navy, #00203f) !important;
  border-color: var(--u-navy, #00203f) !important;
  color: var(--u-paper, #fff) !important;
  box-shadow: var(--ub-cast);
}

/* The wrapper is a `.btn` too, and would otherwise lift the circle it holds a
 * second time. */
html[data-ui="upgrade"] .o_main_nav .o_navlink_background_hover.btn {
  padding: 0 !important;
  min-height: 0;
}

html[data-ui="upgrade"] .o_main_nav .o_navlink_background_hover.btn:hover {
  translate: none;
  background-color: transparent;
}

/* The count itself, which could not be read. `bg-primary` paints the badge
 * navy and says nothing about the figure on it; the theme then paints every
 * badge in the header black and shouts about it —
 *
 *   #wrapwrap:not(.o_header_overlay) header .badge { color: #000 !important }
 *
 * — so the cart carried a 10px black "1" on #00203f (measured at 375px:
 * 18x18, colour rgb(0, 0, 0)). Section 14 gave the badge its navy ground and
 * left the ink alone. Beating a rule that carries both an id and `!important`
 * needs an id of its own; `#wrapwrap` is the origin's, and the only one
 * available. */
html[data-ui="upgrade"] #wrapwrap header .my_cart_quantity {
  color: var(--u-paper, #fff) !important;
}

/* And when the circle under it turns navy, a navy badge disappears into it.
 * Paper badge, navy figure, for the length of the hover. */
html[data-ui="upgrade"] #wrapwrap header a.btn.rounded-circle:hover .my_cart_quantity,
html[data-ui="upgrade"] #wrapwrap header .o_navlink_background_hover:hover .my_cart_quantity {
  background: var(--u-paper, #fff) !important;
  color: var(--u-navy, #00203f) !important;
  box-shadow: 0 0 0 2px var(--u-navy, #00203f);
}

/* The mobile menu's opener is a `.btn` with no variant at all. */
html[data-ui="upgrade"] .navbar .nav-link.btn:not(.btn-close) {
  min-height: 42px;
  min-width: 42px;
  border-radius: 50%;
  transition:
    background-color var(--ub-t) var(--u-ease),
    color var(--ub-t) var(--u-ease);
}

html[data-ui="upgrade"] .navbar .nav-link.btn:not(.btn-close):hover {
  background-color: color-mix(in srgb, var(--u-ink, #292121) 8%, transparent);
  translate: none;
}

/* ==================================================================
 * 22.12 · The quantity stepper
 *
 * Three flush targets inside one pill, the middle one a number. Sections 3
 * and 17 set the box; this sets what happens inside it, which was nothing —
 * the steppers are `.btn-link`, so the theme's cancelled hover left them with
 * no feedback at the exact moment a shopper is changing what they are about
 * to buy.
 * ================================================================== */
html[data-ui="upgrade"] .css_quantity {
  --ub-in-group: 1; /* it carries `input-group`; section 22.19 stands aside */
  border-radius: var(--ub-radius);
  border-color: var(--ub-hair);
  background: var(--ub-surface);
  overflow: hidden;
}

html[data-ui="upgrade"] .css_quantity .btn {
  --btn-padding-x: 0;
  --btn-border-radius: 0;
  min-width: 46px;
  border: 0;
  border-radius: 0;
  background-image: none;
  color: var(--u-ink, #292121);
  transition:
    background-color var(--ub-t) var(--u-ease),
    color var(--ub-t) var(--u-ease);
}

html[data-ui="upgrade"] .css_quantity .btn:hover {
  background-color: color-mix(in srgb, var(--u-navy, #00203f) 10%, transparent);
  color: var(--u-navy, #00203f);
  translate: none;
  box-shadow: none;
}

html[data-ui="upgrade"] .css_quantity .btn:active {
  background-color: color-mix(in srgb, var(--u-navy, #00203f) 18%, transparent);
  translate: none;
  scale: 1;
}

html[data-ui="upgrade"] .css_quantity input.quantity {
  background: transparent;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ==================================================================
 * 22.13 · The search submit
 *
 * It shares an edge with the field it belongs to, so it takes that field's
 * right-hand pill and nothing else — a lifting button welded to an input
 * tears the seam open.
 * ================================================================== */
html[data-ui="upgrade"] .oe_search_button {
  --btn-padding-x: 18px;
  --btn-bg: transparent;
  --btn-border-color: transparent;
  --btn-color: var(--u-ink-2, #574949);
  --btn-hover-bg: var(--u-navy, #00203f);
  --btn-hover-border-color: var(--u-navy, #00203f);
  --btn-hover-color: var(--u-paper, #fff);

  background-image: none;
  box-shadow: none;
}

html[data-ui="upgrade"] .oe_search_button:hover,
html[data-ui="upgrade"] .oe_search_button:active {
  translate: none;
  scale: 1;
  box-shadow: none;
}

/* ==================================================================
 * 22.14 · Close
 *
 * A 16px glyph at 50% opacity, absolutely positioned into a corner, is a
 * target a thumb misses. A circle at the touch floor instead, and the theme's
 * own `--btn-close-white-filter` to turn the glyph paper when the circle
 * turns navy — the mark is a background SVG, so it has to be filtered rather
 * than recoloured.
 * ================================================================== */
html[data-ui="upgrade"] .btn-close {
  width: 40px;
  height: 40px;
  min-height: 40px;
  padding: 0;
  margin: 10px;
  border-radius: 50%;
  background-color: var(--ub-surface);
  background-size: 13px auto;
  opacity: 1;
  color: var(--u-ink, #292121);
  transition:
    background-color var(--ub-t) var(--u-ease),
    filter var(--ub-t) var(--u-ease),
    rotate var(--ub-t-slow) var(--u-out),
    scale var(--ub-t) var(--u-ease);
}

html[data-ui="upgrade"] .btn-close:hover {
  background-color: var(--u-navy, #00203f);
  filter: var(--btn-close-white-filter, invert(1) grayscale(100%) brightness(200%));
  opacity: 1;
  rotate: 90deg;
}

html[data-ui="upgrade"] .btn-close:active {
  scale: 0.94;
}

/* ==================================================================
 * 22.15 · Disabled
 *
 * The theme's 0.65 opacity leaves a navy pill still reading as the brightest
 * thing on the page. Further down, and desaturated, so "not now" is legible
 * without a second look. The cast and the lift go with it — a shadow under an
 * inert control is the wrong promise.
 * ================================================================== */
html[data-ui="upgrade"] .btn:disabled,
html[data-ui="upgrade"] .btn.disabled,
html[data-ui="upgrade"] fieldset:disabled .btn {
  box-shadow: none;
  background-image: none;
  translate: none;
  scale: 1;
  filter: grayscale(0.45);
}

/* ==================================================================
 * 22.16 · The wishlist heart
 *
 * Section 16 already makes it a 36px paper chip on the card. What it has no
 * state for is the thing it is for: the moment it is pressed. The chip lifts
 * and the heart swells, and an added one keeps the navy.
 * ================================================================== */
html[data-ui="upgrade"] .oe_product .o_add_wishlist {
  transition:
    color var(--ub-t) var(--u-ease),
    box-shadow var(--ub-t-slow) var(--u-out),
    scale var(--ub-t-slow) var(--u-out);
}

html[data-ui="upgrade"] .oe_product .o_add_wishlist:hover {
  box-shadow: var(--u-shadow-2, 0 2px 6px rgba(41, 33, 33, 0.06), 0 28px 56px -20px rgba(41, 33, 33, 0.28));
  scale: 1.08;
  translate: none;
}

html[data-ui="upgrade"] .oe_product .o_add_wishlist:active {
  scale: 0.94;
  translate: none;
}

html[data-ui="upgrade"] .oe_product .o_add_wishlist .fa {
  transition: scale 220ms var(--u-out);
}

html[data-ui="upgrade"] .oe_product .o_add_wishlist:hover .fa {
  scale: 1.12;
}

/* The product page's own wishlist toggle is a `.btn-link` with an icon, so it
 * keeps the text register and only the icon answers. */
html[data-ui="upgrade"] .o_add_wishlist_dyn .fa {
  transition:
    scale 220ms var(--u-out),
    color var(--ub-t) var(--u-ease);
}

html[data-ui="upgrade"] .o_add_wishlist_dyn:hover .fa {
  scale: 1.15;
  color: var(--u-navy, #00203f);
}

/* ==================================================================
 * 22.17 · Focus
 *
 * Section 4 puts an outline back on everything focusable. On a pill it wants
 * a soft ring behind it as well, so the focus reads at a glance on a dark
 * fill, where a 2px hairline against navy does not.
 * ================================================================== */
html[data-ui="upgrade"] .btn:focus-visible {
  outline: 2px solid var(--u-navy, #00203f);
  outline-offset: 2px;
  box-shadow: var(--ub-ring);
  border-radius: var(--btn-border-radius);
}

html[data-ui="upgrade"] .btn-primary:focus-visible,
html[data-ui="upgrade"] .btn-group > .btn.active:focus-visible {
  outline-color: var(--u-deep, #14212e);
  box-shadow: var(--ub-cast), var(--ub-ring);
}

/* ==================================================================
 * 22.18 · Asked for less
 *
 * Section 9 flattens every transition duration under `prefers-reduced-motion`,
 * which covers the lift and the underline. What it cannot flatten is a state
 * that is already applied at the end of a hover, so those are given up here.
 * ================================================================== */
@media (prefers-reduced-motion: reduce) {
  html[data-ui="upgrade"] .btn:hover,
  html[data-ui="upgrade"] .btn:active,
  html[data-ui="upgrade"] .btn:first-child:active {
    translate: none;
    scale: 1;
  }

  html[data-ui="upgrade"] .btn-close:hover {
    rotate: none;
  }

  html[data-ui="upgrade"] .oe_product .o_add_wishlist:hover,
  html[data-ui="upgrade"] .oe_product .o_add_wishlist:hover .fa,
  html[data-ui="upgrade"] .o_add_wishlist_dyn:hover .fa {
    scale: 1;
  }
}

/* ==================================================================
 * 22.19 · Welded to a field
 *
 * A button inside an `.input-group` is one half of a control, not a control:
 * it takes the field's radius on the outer edge and nothing at all on the
 * seam, and it does not lift, because half a control cannot leave the other
 * half behind. Measured on the cart's discount row before this: the field
 * ended at 10px and Apply began again at 6.4px, two radii on one object.
 * ================================================================== */
html[data-ui="upgrade"] .input-group:not(.css_quantity) > .btn {
  --btn-border-radius: var(--u-radius, 10px);
  border-radius: 0;
  box-shadow: none;
  background-image: none;
}

html[data-ui="upgrade"] .input-group:not(.css_quantity) > .btn:first-child {
  border-start-start-radius: var(--u-radius, 10px);
  border-end-start-radius: var(--u-radius, 10px);
}

html[data-ui="upgrade"] .input-group:not(.css_quantity) > .btn:last-child {
  border-start-end-radius: var(--u-radius, 10px);
  border-end-end-radius: var(--u-radius, 10px);
}

html[data-ui="upgrade"] .input-group:not(.css_quantity) > .btn:hover,
html[data-ui="upgrade"] .input-group:not(.css_quantity) > .btn:active {
  translate: none;
  scale: 1;
  box-shadow: none;
}
