@layer components {
/* Rating — 5-star input/display.

   Structure:
     .psw-rating                       — vertical stack: optional label + stars + message
     .psw-rating__label                — Label/Medium, center-aligned
     .psw-rating__stars                — row of 5 stars, 4px gap, centered
     .psw-rating__star                 — 40x40 cell; <span> (read-only) or
                                         <label> wrapping a visually-hidden
                                         <input type="radio"> (interactive).
                                         Empty by default (gray-300),
                                         filled when .psw-rating__star--filled
                                         is present.
     .psw-rating__star-svg             — the inline star path; color inherited
                                         via currentColor.
     .psw-rating__star--filled         — committed/checked fill color.
     .psw-rating__star--preview        — transient hover-preview fill (applied
                                         by psw-rating.js).
     .psw-rating--error                — server-rendered error wrapper. Stars
                                         get a red outline; .psw-rating__message
                                         renders below.
     .psw-rating__message              — danger-colored helper text (server-
                                         rendered errors).
     .psw-rating__message--invalid     — same treatment but hidden until any
                                         radio in the group is :user-invalid
                                         (i.e. user submitted without picking).

   Color is driven entirely by the CSS `color` property on .psw-rating__star
   so the SVG (fill="currentColor") follows automatically — no per-state svg
   rules and no JS color manipulation. */

.psw-rating {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);                                   /* 16px between label and stars */
}

.psw-rating__label {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-label-md);                       /* Label/Medium (16/20) */
    line-height: var(--text-label-md--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
    text-align: center;
}

.psw-rating--error .psw-rating__label,
.psw-rating--interactive:has(:user-invalid) .psw-rating__label {
    color: var(--color-danger-400);
}

.psw-rating__stars {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);                                   /* 4px between stars */
}

.psw-rating__star {
    /* Reset for both <span> (read-only) and <label> (interactive). */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--color-gray-300);                          /* empty-star color */
    transition: color 80ms ease-out;
}

.psw-rating__star-svg {
    width: 31.4193px;                                      /* star viewBox width */
    height: 29.9547px;                                     /* star viewBox height */
    display: block;
}

.psw-rating__star--filled {
    color: var(--color-warning-400);                       /* filled-star color */
}

.psw-rating__star--preview {
    color: var(--color-warning-400);                       /* Hover preview tint (applied by JS) */
}

/* --- interactive-only: cursor + focus ring ------------------------------- */

.psw-rating--interactive .psw-rating__star {
    cursor: pointer;
}

/* Focus ring sits on the wrapper because the real <input> is sr-only.
   :has(:focus-visible) routes the inner focus state out to the label. */
.psw-rating--interactive .psw-rating__star:has(:focus-visible) {
    outline: 2px solid var(--color-info-400);
    outline-offset: 2px;
}

/* --- error + validation states ------------------------------------------
   Mirrors psw-field's two-path model:
     Server-rendered  → .psw-rating--error (set by the partial when error=)
     Native HTML5     → :user-invalid (set by the browser after a submit
                                       attempt with required + no selection)
   Both turn the label + message danger-red. The red outline around the stars
   is reserved for the live :user-invalid path only — a server-rendered error
   already reads clearly from the red message, so it skips the stroke (per
   designer review). :user-invalid only fires after user interaction (or a
   failed submit), so the initial render isn't outlined until submit. */

.psw-rating--interactive:has(:user-invalid) .psw-rating__stars {
    outline: 2px solid var(--color-danger-400);
    outline-offset: 4px;
    border-radius: var(--radius-md);
}

/* Message slots — same typography as psw-field__message. */
.psw-rating__message {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-label-sm);
    line-height: var(--text-label-sm--line-height);
    color: var(--color-danger-400);
    text-align: center;
}

/* :user-invalid-driven message is pre-rendered (so the slot is always
   measured into the layout) but hidden until validation actually fails. */
.psw-rating__message--invalid {
    display: none;
}
.psw-rating--interactive:has(:user-invalid) > .psw-rating__message--invalid {
    display: block;
}
}
