/* Bridge between Taskerator's design tokens and UI5's Horizon theme.
   UI5 components read their colours, fonts, and radii from --sap* custom
   properties, which are ordinary inherited custom properties and therefore
   cross the shadow boundary. Overriding them here restyles every UI5 component
   without forking or patching UI5's own CSS.

   The selector is doubled to raise specificity above the :root rules that UI5's
   parameters bundle injects at runtime; without it the injected stylesheet can
   land after this one in the cascade and win the tie.

   Every value below points at a Tailwind theme token rather than repeating a hex
   value, so the palette has one definition: change --color-p in
   app/assets/tailwind/application.css and UI5 follows. This is as close as the
   shadow boundary allows to styling these components with Tailwind; see
   docs/claude_design_handoff/ui5-cheatsheet.md for what utilities can and cannot
   reach. One caveat when adding a mapping: Tailwind 4 only emits theme variables
   that something actually uses, so referencing a token no utility and no rule in
   tokens.css touches will silently resolve to nothing. */
:root:root {
  /* Typography. UI5's default is a 72/Arial stack; the design calls for DM Sans
     everywhere, self-hosted. tokens.css owns the stack and the @font-face rules. */
  --sapFontFamily:       var(--font-body);
  --sapFontLightFamily:  var(--sapFontFamily);
  --sapFontBoldFamily:   var(--sapFontFamily);
  --sapFontSemiboldFamily: var(--sapFontFamily);
  --sapFontSemiboldDuplexFamily: var(--sapFontFamily);
  --sapFontHeaderFamily: var(--sapFontFamily);
  --sapFontSize:         13px;
  --sapFontSmallSize:    11px;
  --sapFontLargeSize:    15px;

  /* Horizon spells out a literal "72" font stack for buttons and object
     headers rather than deriving them from --sapFontFamily, so overriding the
     base family alone leaves those elements on SAP's own typeface. The icon
     font family is deliberately left alone: it addresses the SAP-icons glyph
     font, not body text. */
  --sapButton_FontFamily:            var(--sapFontFamily);
  --sapButton_Emphasized_FontFamily: var(--sapFontFamily);
  --sapObjectHeader_Title_FontFamily: var(--sapFontFamily);
  --sapContent_MonospaceFontFamily:     "JetBrains Mono", "Fira Code", monospace;
  --sapContent_MonospaceBoldFontFamily: var(--sapContent_MonospaceFontFamily);

  /* Brand and text colours. */
  --sapBrandColor:       var(--color-p);
  --sapHighlightColor:   var(--color-p);
  --sapSelectedColor:    var(--color-p);
  --sapActiveColor:      var(--color-p-dk);
  --sapLinkColor:        var(--color-p);
  --sapTextColor:        var(--color-n900);
  --sapTitleColor:       var(--color-n900);
  --sapContent_LabelColor: var(--color-n600);
  --sapContent_FocusColor: var(--color-p);
  --sapNegativeColor:    var(--color-ng);
  --sapPositiveColor:    var(--color-ok);
  --sapCriticalColor:    var(--color-warn);

  /* Surfaces. UI5's Horizon backgrounds are a light blue-grey; the app's
     surfaces are the neutral ramp. */
  --sapBackgroundColor:  var(--color-n50);
  --sapGroup_ContentBackground: var(--color-n0);
  --sapTile_Background:  var(--color-n0);

  /* Buttons. Default (secondary) buttons are outlined on a white surface;
     Emphasized is the brand fill. */
  --sapButton_Background:        var(--color-n0);
  --sapButton_BorderColor:       var(--color-n300);
  --sapButton_TextColor:         var(--color-n900);
  --sapButton_Hover_Background:  var(--color-n100);
  --sapButton_Hover_BorderColor: var(--color-n400);
  --sapButton_Active_Background: var(--color-n200);
  --sapButton_Active_BorderColor: var(--color-n400);

  --sapButton_Emphasized_Background:        var(--color-p);
  --sapButton_Emphasized_BorderColor:       var(--color-p);
  --sapButton_Emphasized_TextColor:         var(--color-n0);
  --sapButton_Emphasized_Hover_Background:  var(--color-p-dk);
  --sapButton_Emphasized_Hover_BorderColor: var(--color-p-dk);
  --sapButton_Emphasized_Active_Background: var(--color-p-dk);

  /* Transparent buttons back the handoff's "ghost" treatment: no chrome, neutral
     text. Horizon's default tints them with the brand colour, which reads as a
     second call to action next to a real one. */
  --sapButton_Lite_Background:        transparent;
  --sapButton_Lite_BorderColor:       transparent;
  --sapButton_Lite_TextColor:         var(--color-n600);
  --sapButton_Lite_Hover_Background:  var(--color-n100);
  --sapButton_Lite_Hover_BorderColor: transparent;
  --sapButton_Lite_Hover_TextColor:   var(--color-n700);
  --sapButton_Lite_Active_Background: var(--color-n200);
  --sapButton_Lite_Active_BorderColor: transparent;

  /* Positive buttons. Horizon's Accept treatment is a pale yellow-green fill with
     dark green text; the design defines exactly one green, .btn-success, which is
     a solid --ok with white text. There is no outlined green in the design at all,
     so a green action is a filled one. */
  --sapButton_Accept_Background:        var(--color-ok);
  --sapButton_Accept_BorderColor:       var(--color-ok);
  --sapButton_Accept_TextColor:         var(--color-n0);
  --sapButton_Accept_Hover_Background:  #146c43;
  --sapButton_Accept_Hover_BorderColor: #146c43;
  --sapButton_Accept_Hover_TextColor:   var(--color-n0);
  --sapButton_Accept_Active_Background: #146c43;
  --sapButton_Accept_Active_BorderColor: #146c43;

  --sapButton_Reject_Background:       var(--color-n0);
  --sapButton_Reject_BorderColor:      var(--color-ng);
  --sapButton_Reject_TextColor:        var(--color-ng);
  --sapButton_Reject_Hover_Background: var(--color-ng-lt);
  --sapButton_Reject_Hover_BorderColor: var(--color-ng);

  /* Fields (input, select, textarea). */
  --sapField_Background:              var(--color-n0);
  --sapField_BorderColor:             var(--color-n300);
  --sapField_TextColor:               var(--color-n900);
  --sapField_PlaceholderTextColor:    var(--color-n500);
  --sapField_Hover_Background:        var(--color-n0);
  --sapField_Hover_BorderColor:       var(--color-n400);
  --sapField_Focus_BorderColor:       var(--color-p);
  --sapField_Active_BorderColor:      var(--color-p);

  /* Radii. Horizon's defaults are rounder than the app's; map them onto the
     token scale in tailwind/application.css. */
  --sapButton_BorderCornerRadius: 6px;
  --sapField_BorderCornerRadius:  6px;
  --sapElement_BorderCornerRadius: 6px;
  --sapPopover_BorderCornerRadius: 10px;
  --sapElement_BorderWidth:       1px;

  /* The design draws every button outline at 1.5px (see the .btn rule in the
     "Taskerator Desktop Settings" mockup); Horizon's default is 1px, which was
     recorded as a cosmetic delta when the task detail sheet was converted. The
     button border is its own parameter, so it can match without thickening
     field borders too. */
  --sapButton_BorderWidth:        1.5px;

  /* Overlay shadows. Horizon's dialog shadow is an 80px blur at 25% opacity,
     which over the modal block layer's dimming reads as a heavy grey slab
     around the dialog rather than as a shadow. The app's overlay token is much
     tighter and already carries the hairline ring Horizon puts in the same
     parameter. Shadow1 backs menus and suggestion popovers, Shadow3 the modal
     dialog; the token scale draws no distinction between them, so they share
     one value here. */
  --sapContent_Shadow1: var(--shadow-dropdown);
  --sapContent_Shadow3: var(--shadow-dropdown);

  /* Density. Horizon sizes controls for a desktop business app; the sheet's
     rows are tighter than that. */
  --sapElement_Height:         30px;
  --sapElement_Compact_Height: 26px;
  --sapContent_IconHeight:     1rem;
}

/* Cloak UI5 elements until their definitions register.

   This is needed, and it was measured rather than assumed. On a cold load of a
   page containing UI5 elements, throttled to six times slower CPU and a slow
   connection, first contentful paint lands around 230ms while the bundle finishes
   registering its elements around 450ms. In that window an undefined custom
   element is an ordinary inline box, so a ui5-button paints its slotted label as
   bare unstyled text. Without this rule that is roughly 200ms of visible garbage.

   Turbo Frame navigations do not need equivalent handling: UI5 registers every
   element eagerly when the bundle executes, so anything arriving in a later frame
   is already defined. Measured at zero undefined paints on frame load.

   Every element the bundle registers is listed: a partial list silently leaves the
   omitted ones flashing. Keep this in step with the imports in
   app/javascript/ui5.js.

   Tests render as fast as possible, so the layout omits the cloak class there. */
.ui5-cloak ui5-button:not(:defined),
.ui5-cloak ui5-checkbox:not(:defined),
.ui5-cloak ui5-dialog:not(:defined),
.ui5-cloak ui5-icon:not(:defined),
.ui5-cloak ui5-input:not(:defined),
.ui5-cloak ui5-message-strip:not(:defined),
.ui5-cloak ui5-option:not(:defined),
.ui5-cloak ui5-panel:not(:defined),
.ui5-cloak ui5-radio-button:not(:defined),
.ui5-cloak ui5-segmented-button:not(:defined),
.ui5-cloak ui5-segmented-button-item:not(:defined),
.ui5-cloak ui5-select:not(:defined),
.ui5-cloak ui5-switch:not(:defined),
.ui5-cloak ui5-tag:not(:defined),
.ui5-cloak ui5-textarea:not(:defined) {
  visibility: hidden;
}

/* Modal dialog backdrop.

   UI5 opens dialogs as popover="manual", so the dim behind them comes from the
   ::backdrop pseudo-element rather than from an element we can inspect. Left to
   UI5 it computes to opaque black and paints darker than the handoff's overlay
   dim. Pinning it to the viewport and giving it the intended alpha is enough;
   see the migration brief for the residual artifact this does not fix. */
ui5-dialog::backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
}

/* Button padding.

   Horizon gives a button 9px of horizontal padding, against the 14px the design's
   own .btn rule uses, which is what makes the label sit close to the outline. The
   height is fine and comes from --sapElement_Height, a public parameter set above;
   the padding has no public parameter, only a private --_ui5_button_base_padding.
   The component exposes its inner button as a part, so the padding is set there
   instead of by reaching into a private variable that a UI5 upgrade may rename. */
ui5-button::part(button) {
  padding-inline: 0.875rem;
}

/* The count badge inside the filter's segmented button.

   A tag is sized for standing alone, so at 24px inside a 30px segment it read as
   a second control rather than as a count on a label. Scoped to the segmented
   button, since a tag used on its own elsewhere should keep its normal size. */
ui5-segmented-button ui5-tag {
  /* A tag is an inline-block sitting on the text baseline, so its box hangs from
     the baseline of the number inside it rather than centring on the label beside
     it, and it rides visibly high. The number itself is centred within the pill,
     which is why only the pill looks wrong. */
  vertical-align: middle;
}

ui5-segmented-button ui5-tag::part(root) {
  /* Shrink the pill by taking the padding out and letting the height follow from
     the line box, rather than by clamping the height. The root is a flex box on
     align-items: baseline with 3px of vertical padding around a 16px line, so a
     forced height smaller than that left the number sitting low in a content box
     too short to hold its own line. */
  padding-block: 0;
  padding-inline: 0.375rem;
  line-height: 1.125rem;
  align-items: center;
}

/* Pairing two buttons into one control.

   UI5 has no button-group component, so a primary action with a menu trigger
   beside it renders as two independently rounded rectangles sitting flush, and
   the facing corners curve away from each other into a visible notch. Squaring
   the shared edge and pulling the second button over the first by the width of
   its border makes the pair read as one rounded control with a divider through
   it, which is what a button group looked like.

   The radius has to be set on the host and on the inner button part: the host
   draws the border and the part draws the fill, and a radius on only one of them
   leaves the other's corner showing. */
ui5-button.segment-start,
ui5-button.segment-start::part(button) {
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}

ui5-button.segment-end,
ui5-button.segment-end::part(button) {
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}

ui5-button.segment-end {
  margin-inline-start: -1px;
}

/* The divider. Both halves are the same fill, so the shared border is invisible
   without one. */
ui5-button.segment-end::part(button) {
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.4);
}

/* Button focus ring: keyboard only.

   Same story as the checkbox below. On desktop UI5 draws the ring from
   :focus-within rather than :focus-visible, so clicking a button leaves a ring
   around it afterwards. That is most obvious on the menu trigger in Focus Mode,
   where the ring stays for as long as the menu is open and outlines the trigger
   as a separate rounded box, undoing the pairing with the button beside it. */
ui5-button::part(button)::after {
  display: none;
}

ui5-button::part(button):focus-visible::after {
  display: block;
}

/* Checkbox focus ring: keyboard only.

   UI5 draws the ring for plain :focus as well as :focus-visible, so clicking a
   checkbox left a box around it afterwards. That is noise — the user just pointed
   at the thing and can see the tick — and it was the more noticeable for focus now
   surviving the row being replaced by a Turbo Stream.

   Suppressing it for pointer focus and keeping it for :focus-visible is the whole
   fix. A browser matches :focus-visible for keyboard focus but not for a mouse
   click on a control like this, which is exactly the distinction worth drawing:
   without a ring there is no way to tell where Tab has landed. Measured on the
   checkbox itself — after a click the root has :focus and not :focus-visible;
   after Tab it has both. Note that the pseudo-class has to be applied to the part
   rather than to the host, because focus is delegated into the shadow root and
   the host never matches :focus-visible at all.

   The ring is a ::before on the checkbox's root, sized and coloured through
   private parameters, so no theming parameter reaches it. The component exposes
   its root as a part, which is the supported handle, and an outer rule on that
   part's pseudo-element wins over the shadow tree's own. It is also rounded to
   match the control, since UI5 draws a hard square around a rounded box. */
ui5-checkbox::part(root)::before {
  display: none;
}

ui5-checkbox::part(root):focus-visible::before {
  display: block;
  border-radius: var(--radius-md);
  border-color: var(--color-p);
}

/* Icon sizing. UI5 icons default to 1rem square and take their colour from
   the surrounding text unless told otherwise, which is what the sheet's
   inline label rows want.

   flex-shrink is pinned because most of these icons sit inside flex rows next
   to text that can grow. A ui5-icon is an ordinary flex item, so its width is
   a basis rather than a floor, and a long enough task title would squeeze it
   from 12px down to a few pixels while leaving its height alone. The result
   read as an icon that changed size from row to row. Icons should never be the
   thing that gives way. */
ui5-icon {
  color: currentColor;
  flex-shrink: 0;
}

