# 240 — skin.milionar.modern: the modern look

Status: built and verified in the live Kodi 2026-10-15 (Standa's goal: "a second skin, a modern facelift with glass,
rounded, glossy and blur effects, every function kept, selectable in the settings, released").

## 1. What it is

A second Kodi skin, `skin.milionar.modern` ("Milionář Modern"), with every function of `skin.milionar` and another
look: rounded glass panels, blurred ambient light from the selected title's picture, glossy buttons, soft shadows,
rounded posters and calmer animations. For the addon both skins are the same skin (`wcs/skin/family.py`); they
share one version and Kodi keeps their skin settings apart, so a switch carries the settings along.

## 2. No copy: an overlay composed on the device

`resources/skin.milionar.modern/` holds only what differs (≈40 files, ~260 kB):

| Part | Where | What |
|------|-------|------|
| manifest | `addon.xml` | id, name, texts; version and plugin dependency kept equal to the classic skin by `deploy_check.py` |
| recipe | `compose.json` | includes first, texture swaps, poster masks, colours, exact patches |
| includes | `xml/Includes_Modern.xml` | overrides of whole includes (Kodi keeps the first include of a name) |
| textures | `media/modern/*`, `media/milionar/cinema/*` | glass shapes; cinematic textures with the modern radius (only those that differ) |

`wcs/skin/compose.py` (pure Python) builds the skin from the classic tree and the overlay: overlay files replace
classic ones, `Includes_Modern.xml` is included first in `Includes.xml`, XML references to Estuary textures are
swapped (`"old@tint"` swaps only elements with that `colordiffuse`, e.g. the dialog rows `colors/white.png@dialog_tint`),
an element that names a swapped texture gets the new 9-slice `border`, posters get a rounded `diffuse` mask, colours
are replaced or added in `colors/defaults.xml`, and patches edit structural details no include reaches (the Home
sidebar panel and its focus, the card lift in the generated cinematic rows). Every patch states how often its text
occurs: a changed classic skin fails the composition (and `test_modern.py`) instead of looking wrong.

The installer (`wcs/skin/installer.py`) installs both skins on every addon update: the classic one is copied, the
modern one is composed into the staging folder; both get the Estuary runtime files (XBT, fonts, logo) and the same
validate-then-rename swap. A failing modern composition leaves the classic skin working. In development
`scripts/skin_harness/sync_dev.sh` (→ `compose_modern.py`) composes into the installed copy.

## 3. The workarounds (Kodi has no blur and no rounded-corner shader)

1. **Blur = Kodi's own thumbnail.** `hero.blur(url)` turns a backdrop into
   `image://<url-encoded>/transform?width=32&height=18` (TMDb's `w300` size as the source). Kodi's texture cache
   (`TextureCacheJob::DecodeImageURL`, options `width`/`height`) scales it to 32×18 once and caches it; the skin
   stretches it over the screen with linear filtering. A second copy shifted by half a texel at 50 % evens out the
   bilinear cells. Cost: one ~20 kB download and a few hundred bytes of cache per title. The items carry it as
   `milionar.hero.blur` (`hero._texts`, `hero.challenge`, `vinyl/presentation.fields`), the detail as
   `milionar.detail.blur`; `gen_cinema.py` generates `$VAR[MilCineBlur]` next to `MilCineFanart`
   (`row_cache.VERSION` 4).
2. **Frosted glass = translucent panel over the blurred light.** Glass textures (`gen_modern_textures.py`) are
   translucent navy with a lighter top, a hairline edge that catches the light and a soft gloss; over the ambient
   light they read as frosted glass. Dialog panels and their rows are nearly opaque (glass ~95 %, `dialog_tint`
   F8): Kodi cannot blur what lies behind a dialog, and sharp Home text showing through would be noise. The sharp
   Home backdrop gets a dark scrim behind the hero text (`modern/hero_scrim.png`), so white text stays readable on
   a bright picture; poster labels (seasons, cast) sit on a deeper dark foot (`poster_shade`).
3. **Rounded corners = 9-slice textures and diffuse masks.** Every shape keeps its radius at any size through
   Kodi's `border`; the border each texture needs is in `compose.json` (`test_modern.py` checks it fits).
   Posters get `diffuse="modern/mask_poster.png"`.
4. **Gloss = light textures tinted by `colordiffuse`.** Focus textures are light grey with a white top, so the
   accent colour keeps a sheen; the focus glow lies in the 20 px padding Estuary's buttons already have.
5. **Shadows** are rounded (`modern/shadow.png` for Estuary's `bordertexture`, drop shadows inside the panel
   textures).
6. **Motion**: the focused Home card lifts (zoom 105 %, cubic), dialogs open with a gentle spring.
7. **Glow without clipping.** A Kodi panel draws its items only inside its own rect, so the lifted card's frame and
   glow would be cut flat. `gen_cinema.Look` generates the modern rows with `room` above and below the cards (the
   panel starts higher and is taller, the cards move back, the "more" arrow stays level with them) and a focus
   frame that reaches `glow` (12) px beyond Estuary's 16 px with a soft, wide glow (`focus.png` 120 px, ring 25 px
   in). The classic files stay byte-identical.
8. **Text fields** (keyboard, numeric dialog) are a rounded dark well (`modern/field.png` for Estuary's
   `button-alt-nofo`); the numeric dialog's entry is a rounded tile (`colors/white.png@32FFFFFF`).
9. **Round cards** (records, artists) get the same glow as the other cards: `focus_circle.png` with the ring 28 px in
   (`circle_ring`).
10. **No cut glow.** A group list draws its children only inside its height: the sidebar's round buttons (Home 700),
    the touch buttons and the numeric dialog's arrows sit in group lists as tall as the buttons. `test_modern.py`
    checks every group list against its buttons and that every glow fades out inside its texture.

## 3a. Home sidebar (modern only, 2026-10-15)

Standa: the collapsed rail's icons were not centred; the modern skin gets its own sidebar, a TV app's rules.

- **Floating glass**, 16 px off the screen's edges, ending 22 px under the last menu item (`ModernSidebarGlass`,
  one image per number of items, `Container(9000).NumItems`; 9 and more reach the screen's bottom).
- **Collapsed**: a rail 16-112; the logo mark and the icons on its axis (x 64: `MilionarSidebarIconNudge` moves
  Estuary's 95 px icon squares by 20, `MilionarSidebarLogoCollapse` slides the logo by 53). Idle icons at 65 %
  (`sidebar_idle`), the current section gold on a soft gold pill: the open panel's focus pill narrowed to the rail (same texture, 8 px
  inside the glass, x 24-104), so opening only widens it (`ModernSidebarActive`, `sidebar_active`; a 60 px tile
  hugging the icon looked like a mistake, Standa).
- **Expanded**: a panel 16-446 grows out of the rail (slides out by 334 px and fades in while the rail fades out).
  The icons keep their column, only the labels come; labels, buttons and the gold focus pill (8 px inside the
  glass, `menu_focus.png`) fade in once the panel is out (`MilionarSidebarFade` with a delay). The content behind
  dims (`sidebar_scrim`), so the eye follows the focus. Logo and the round buttons (power, settings, search) are
  centred in the panel.
- The floating panel ends at 446, the backdrop starts at 462: its diffuse mask fades the left edge in from nothing
  (`hero_fade(edge=0.08)`), else its 12 % floor showed as a hard line by the panel (`test_modern` checks it).
- The geometry of Home and its content (`Includes_MilionarSidebar.xml`) is the classic's; the composer patches
  Home.xml (panel, focus pill, idle icons, active tile, buttons' row) and the overlay's includes win.

## 4. Choosing the look

Addon settings → Hlavní nastavení → "Vzhled skinu Milionář": Klasický / Moderní (`skin_variant`). The button "Přepnout na
skin Milionář" and the offer after an update switch to the chosen look; changing the choice while a Milionář skin
runs switches at once (`installer.apply_variant` from the service's settings event; `skin_variant_applied` keeps a
later unrelated settings change from switching again). Kodi then asks "Keep this skin?" as for any skin. Both skins
are also listed in Kodi's own skin setting. Every addon check of "our skin" accepts both (`family.active()`).

## 5. Verification (2026-10-15, live Kodi 2560×1440)

Home (expanded and collapsed sidebar, ambient light, card lift), Home detail, Pro tebe / Milionář / Vinyl sections,
settings menu 1108, widget manager 1123, card context menu, Kodi settings and a category, addon settings dialog,
yes/no and select dialogs, power menu, notification, search 1120 (rounded posters), video OSD and info (local ffmpeg
test video), and switching Moderní → Klasický → Moderní from the addon setting. Offline: `test_modern.py` (10),
`test_installer_performance.py` (24), `tests.test_release` (13).
