Last modified by Veronika Kocher on Friday, 18 Sep 2026 16:47

From version 39.22
edited by Veronika Kocher
on Thursday, 03 Sep 2026 9:34
Change comment: There is no comment for this version
To version 39.24
edited by Veronika Kocher
on Thursday, 03 Sep 2026 13:04
Change comment: There is no comment for this version

Summary

Details

Page properties
Content
... ... @@ -640,23 +640,100 @@
640 640  )))
641 641  
642 642  {{html clean="false"}}
643 -<!-- SN_HORIZONTAL_BOX_HARMONIZER_START -->
644 -<style data-sn-horizontal-box-harmonizer="4">
645 -[data-sn-equal-height-managed],
646 -[data-sn-first-section-aligned]{
643 +<!-- SN_SERVICE_NAVIGATOR_MASTER_STYLE_START -->
644 +
645 +<style data-sn-service-navigator-master="1">
646 +/* =========================================================
647 + MASTER TYPOGRAPHY
648 + ========================================================= */
649 +#xwikicontent h1,
650 +#xwikicontent h2,
651 +#xwikicontent h3,
652 +#xwikicontent h4,
653 +#xwikicontent h5,
654 +#xwikicontent h6,
655 +#xwikicontent .box-title,
656 +#xwikicontent [role="heading"],
657 +#xwikicontent h1 a,
658 +#xwikicontent h2 a,
659 +#xwikicontent h3 a,
660 +#xwikicontent h4 a,
661 +#xwikicontent h5 a,
662 +#xwikicontent h6 a,
663 +#xwikicontent .box-title a,
664 +#xwikicontent [role="heading"] a{
665 + text-align:left!important;
666 + text-justify:auto!important;
667 + word-spacing:normal!important;
668 +}
669 +
670 +/* =========================================================
671 + COMING SOON / IN VORBEREITUNG
672 + ========================================================= */
673 +.sn-coming-soon-badge{
674 + display:inline-block;
675 + vertical-align:middle;
676 + margin-left:10px;
677 + padding:4px 8px;
678 + border-radius:14px;
679 + border:1px solid #efd9cc;
680 + background:#fff4ee;
681 + color:#8a4d36;
682 + font-size:11px;
683 + font-weight:700;
684 + letter-spacing:.055em;
685 + line-height:1.2;
686 + white-space:nowrap;
687 +}
688 +
689 +/* =========================================================
690 + NATIVE / DETAIL CALLOUT GEOMETRY
691 + Preserve existing backgrounds.
692 + ========================================================= */
693 +#xwikicontent .arsn-info,
694 +#xwikicontent .dmp-info,
695 +#xwikicontent .ido-info,
696 +#xwikicontent .pp-info,
697 +#xwikicontent .infomessage{
698 + box-sizing:border-box!important;
699 + border-left:4px solid #21b6d7!important;
700 + border-radius:8px;
701 + text-align:left!important;
702 + word-spacing:normal!important;
703 +}
704 +
705 +#xwikicontent .arsn-warn,
706 +#xwikicontent .dmp-warn,
707 +#xwikicontent .ido-warn,
708 +#xwikicontent .pp-warn,
709 +#xwikicontent .warn,
710 +#xwikicontent .warning,
711 +#xwikicontent .warningmessage,
712 +#xwikicontent .errormessage{
713 + box-sizing:border-box!important;
714 + border-left:4px solid #e94b13!important;
715 + border-radius:8px;
716 + text-align:left!important;
717 + word-spacing:normal!important;
718 +}
719 +
720 +[data-sn-master-equal-height],
721 +[data-sn-master-first-row]{
647 647   box-sizing:border-box;
648 648  }
649 649  </style>
650 -<script data-sn-horizontal-box-harmonizer="4">
725 +
726 +<script data-sn-service-navigator-master="1">
651 651  (function(){
652 652   "use strict";
653 653  
730 + var PAGE_KIND="overview";
731 + var PLACEHOLDER=false;
732 + var PLACEHOLDER_LABEL="COMING SOON";
733 +
654 654   var MOBILE_MAX=767;
655 655   var ROW_TOLERANCE=6;
656 - var MIN_W=150;
657 - var MIN_H=36;
658 - var MAX_PARENT_CHILDREN=16;
659 - var SEARCH_DEPTH=5;
736 + var RESIZE_DEBOUNCE=220;
660 660  
661 661   var root=
662 662   document.getElementById("xwikicontent")||
... ... @@ -666,93 +666,105 @@
666 666  
667 667   if(!root)return;
668 668  
669 - var heightManaged=new Set();
670 - var sectionManaged=new Set();
671 - var originalMinHeight=new WeakMap();
672 - var originalMarginTop=new WeakMap();
673 - var raf=0;
746 + var managedHeight=new Set();
747 + var managedMargin=new Set();
748 + var originalHeight=new WeakMap();
749 + var originalMargin=new WeakMap();
674 674  
675 - function num(v){
676 - var n=parseFloat(v);
677 - return Number.isFinite(n)?n:0;
678 - }
751 + var resizeTimer=0;
752 + var running=false;
679 679  
680 - function visible(el,minW,minH){
681 - if(!el||el.nodeType!==1)return false;
682 -
683 - var s=getComputedStyle(el);
684 -
685 - if(
686 - s.display==="none"||
687 - s.visibility==="hidden"||
688 - Number(s.opacity)===0
689 - ){
690 - return false;
754 + var THEMES=[
755 + {
756 + name:"BLUE",
757 + accent:"#21b6d7",
758 + bg:"#eaf7fb",
759 + text:"#315d68",
760 + labels:[
761 + "HIER STARTEN",
762 + "ENTSCHEIDEN",
763 + "ÜBERPRÜFEN & ANPASSEN",
764 + "START HERE",
765 + "DECIDE",
766 + "REVIEW & ADAPT"
767 + ]
768 + },
769 + {
770 + name:"RED",
771 + accent:"#e94b13",
772 + bg:"#fff1ec",
773 + text:"#8a4d36",
774 + labels:[
775 + "PLANEN & ORGANISIEREN",
776 + "ORGANISIEREN & DOKUMENTIEREN",
777 + "PUBLIZIEREN & PRÄSENTIEREN",
778 + "PLAN & ORGANISE",
779 + "PLAN & ORGANIZE",
780 + "ORGANISE & DOCUMENT",
781 + "ORGANIZE & DOCUMENT",
782 + "PUBLISH & PRESENT"
783 + ]
784 + },
785 + {
786 + name:"YELLOW",
787 + accent:"#f0aa00",
788 + bg:"#fff8e7",
789 + text:"#785b17",
790 + labels:[
791 + "SCHÜTZEN & KLÄREN",
792 + "STANDARDISIEREN & IDENTIFIZIEREN",
793 + "ÖFFNEN, NACHNUTZEN & AUFFINDBAR MACHEN",
794 + "PROTECT & CLARIFY",
795 + "STANDARDISE & IDENTIFY",
796 + "STANDARDIZE & IDENTIFY",
797 + "OPEN, REUSE & MAKE FINDABLE"
798 + ]
799 + },
800 + {
801 + name:"GREEN",
802 + accent:"#38b76a",
803 + bg:"#edf9f1",
804 + text:"#3f7451",
805 + labels:[
806 + "INFRASTRUKTUR EINRICHTEN",
807 + "INFRASTRUKTUR & ZUSAMMENARBEIT EINRICHTEN",
808 + "SPEICHERN & ZUGRIFF STEUERN",
809 + "REPOSITORIUM, ERHALTEN & ÜBERGEBEN",
810 + "SET UP INFRASTRUCTURE",
811 + "SET UP INFRASTRUCTURE & COLLABORATION",
812 + "STORE & CONTROL ACCESS",
813 + "REPOSITORY, PRESERVE & HAND OVER"
814 + ]
691 691   }
816 + ];
692 692  
693 - var r=el.getBoundingClientRect();
694 -
695 - return (
696 - r.width>=(minW||1)&&
697 - r.height>=(minH||1)
698 - );
818 + function num(value){
819 + var n=parseFloat(value);
820 + return Number.isFinite(n)?n:0;
699 699   }
700 700  
701 - function excluded(el){
702 - return (
703 - !el.matches||
704 - el.matches(
705 - "button,input,select,textarea,option,label,img,svg,canvas,table,thead,tbody,tfoot,tr,td,th,summary,script,style,noscript,template,.btn,.chip,[role=button]"
706 - )
707 - );
823 + function norm(value){
824 + return String(value||"")
825 + .replace(/\s+/g," ")
826 + .trim()
827 + .toUpperCase();
708 708   }
709 709  
710 - function namedLikeBox(el){
711 - var c=
712 - typeof el.className==="string"
713 - ? el.className
714 - : "";
830 + function visible(el){
831 + if(!el||el.nodeType!==1)return false;
715 715  
716 - return /(^|[s_-])(box|card|tile|panel|role|check|service|source|call|area|detail|resource|topic|warn|info)([s_-]|$)/i.test(c);
717 - }
833 + var style=getComputedStyle(el);
834 + var rect=el.getBoundingClientRect();
718 718  
719 - function boxLike(el){
720 - if(!visible(el,MIN_W,MIN_H)||excluded(el))return false;
721 -
722 - var s=getComputedStyle(el);
723 - var r=el.getBoundingClientRect();
724 -
725 - var border=
726 - num(s.borderTopWidth)+
727 - num(s.borderRightWidth)+
728 - num(s.borderBottomWidth)+
729 - num(s.borderLeftWidth);
730 -
731 - var radius=Math.max(
732 - num(s.borderTopLeftRadius),
733 - num(s.borderTopRightRadius),
734 - num(s.borderBottomLeftRadius),
735 - num(s.borderBottomRightRadius)
736 - );
737 -
738 - var bg=
739 - s.backgroundColor&&
740 - s.backgroundColor!=="rgba(0, 0, 0, 0)"&&
741 - s.backgroundColor!=="transparent";
742 -
743 - var shadow=
744 - s.boxShadow&&
745 - s.boxShadow!=="none";
746 -
747 747   return (
748 - namedLikeBox(el)||
749 - border>=1||
750 - (bg&&radius>=3)||
751 - (shadow&&r.width>=180)
837 + style.display!=="none"&&
838 + style.visibility!=="hidden"&&
839 + rect.width>20&&
840 + rect.height>4
752 752   );
753 753   }
754 754  
755 - function saveOriginal(el,map,property){
844 + function saveStyle(el,map,property){
756 756   if(map.has(el))return;
757 757  
758 758   map.set(el,{
... ... @@ -761,7 +761,7 @@
761 761   });
762 762   }
763 763  
764 - function restoreOriginal(el,map,property){
853 + function restoreStyle(el,map,property){
765 765   var original=map.get(el);
766 766  
767 767   if(original&&original.value){
... ... @@ -775,247 +775,249 @@
775 775   }
776 776   }
777 777  
778 - function reset(){
779 - heightManaged.forEach(function(el){
780 - restoreOriginal(
867 + function resetRuntime(){
868 + managedHeight.forEach(function(el){
869 + restoreStyle(
781 781   el,
782 - originalMinHeight,
871 + originalHeight,
783 783   "min-height"
784 784   );
785 -
786 786   el.removeAttribute(
787 - "data-sn-equal-height-managed"
875 + "data-sn-master-equal-height"
788 788   );
789 789   });
790 790  
791 - sectionManaged.forEach(function(el){
792 - restoreOriginal(
879 + managedMargin.forEach(function(el){
880 + restoreStyle(
793 793   el,
794 - originalMarginTop,
882 + originalMargin,
795 795   "margin-top"
796 796   );
797 -
798 798   el.removeAttribute(
799 - "data-sn-first-section-aligned"
886 + "data-sn-master-first-row"
800 800   );
801 801   });
802 802  
803 - heightManaged.clear();
804 - sectionManaged.clear();
890 + managedHeight.clear();
891 + managedMargin.clear();
805 805   }
806 806  
807 - function pickTarget(child){
808 - if(!visible(child,MIN_W,MIN_H))return null;
894 + function themeFor(text){
895 + var value=norm(text);
809 809  
810 - if(boxLike(child)){
811 - return child;
897 + for(var i=0;i<THEMES.length;i++){
898 + for(var j=0;j<THEMES[i].labels.length;j++){
899 + var label=THEMES[i].labels[j];
900 +
901 + if(
902 + value===label||
903 + value.indexOf(label+" ")===0||
904 + value.indexOf(label+" -")===0||
905 + value.indexOf(label+" ·")===0
906 + ){
907 + return THEMES[i];
908 + }
909 + }
812 812   }
813 813  
814 - var cr=child.getBoundingClientRect();
815 - var q=[];
912 + return null;
913 + }
816 816  
817 - Array.from(child.children||[]).forEach(function(el){
818 - q.push({el:el,depth:1});
819 - });
915 + /* =======================================================
916 + PLACEHOLDER BADGE
917 + ======================================================= */
820 820  
821 - var best=null;
822 - var bestScore=-Infinity;
919 + function applyPlaceholderBadge(){
920 + if(!PLACEHOLDER)return;
823 823  
824 - while(q.length){
825 - var item=q.shift();
826 - var el=item.el;
922 + if(
923 + document.querySelector(
924 + ".sn-coming-soon-badge"
925 + )
926 + ){
927 + return;
928 + }
827 827  
828 - if(!visible(el,MIN_W,MIN_H))continue;
930 + var title=
931 + document.querySelector(
932 + "#document-title"
933 + )||
934 + document.querySelector(
935 + "h1"
936 + );
829 829  
830 - var r=el.getBoundingClientRect();
938 + if(!title)return;
831 831  
832 - if(
833 - r.width>=cr.width*.68&&
834 - boxLike(el)
835 - ){
836 - var widthFit=
837 - 1-Math.min(
838 - 1,
839 - Math.abs(cr.width-r.width)/
840 - Math.max(1,cr.width)
841 - );
940 + var badge=
941 + document.createElement(
942 + "span"
943 + );
842 842  
843 - var topFit=
844 - 1-Math.min(
845 - 1,
846 - Math.abs(cr.top-r.top)/100
847 - );
945 + badge.className=
946 + "sn-coming-soon-badge";
848 848  
849 - var score=
850 - widthFit*6+
851 - topFit*3+
852 - Math.min(r.height,900)/450+
853 - Math.min(r.width*r.height,900000)/900000;
948 + badge.textContent=
949 + PLACEHOLDER_LABEL;
854 854  
855 - if(score>bestScore){
856 - best=el;
857 - bestScore=score;
858 - }
859 - }
951 + badge.setAttribute(
952 + "aria-label",
953 + PLACEHOLDER_LABEL
954 + );
860 860  
861 - if(item.depth<SEARCH_DEPTH){
862 - Array.from(el.children||[]).forEach(function(next){
863 - q.push({
864 - el:next,
865 - depth:item.depth+1
866 - });
867 - });
868 - }
869 - }
870 -
871 - return best;
956 + title.appendChild(
957 + badge
958 + );
872 872   }
873 873  
874 - function groupsByVisualRow(entries){
875 - var groups=[];
961 + /* =======================================================
962 + DETAIL PAGE OWN-ACCENT PROPAGATION
963 + ======================================================= */
876 876  
877 - entries
878 - .slice()
879 - .sort(function(a,b){
880 - return (
881 - a.rect.top-b.rect.top||
882 - a.rect.left-b.rect.left
883 - );
884 - })
885 - .forEach(function(entry){
886 - var group=null;
965 + function parseRGB(value){
966 + var match=String(value||"").match(
967 + /rgba?\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)/i
968 + );
887 887  
888 - for(var i=0;i<groups.length;i++){
889 - if(
890 - Math.abs(
891 - groups[i].top-
892 - entry.rect.top
893 - )<=ROW_TOLERANCE
894 - ){
895 - group=groups[i];
896 - break;
897 - }
898 - }
970 + if(!match)return null;
899 899  
900 - if(!group){
901 - group={
902 - top:entry.rect.top,
903 - entries:[]
904 - };
905 - groups.push(group);
906 - }
972 + return [
973 + Number(match[1]),
974 + Number(match[2]),
975 + Number(match[3])
976 + ];
977 + }
907 907  
908 - group.entries.push(entry);
909 - });
910 -
911 - return groups;
979 + function distance(a,b){
980 + return Math.sqrt(
981 + Math.pow(a[0]-b[0],2)+
982 + Math.pow(a[1]-b[1],2)+
983 + Math.pow(a[2]-b[2],2)
984 + );
912 912   }
913 913  
914 - function validHorizontalGroup(group){
915 - if(group.entries.length<2)return false;
987 + function paletteMatch(cssColor){
988 + var rgb=parseRGB(cssColor);
916 916  
917 - var lefts=
918 - group.entries
919 - .map(function(e){return e.rect.left;})
920 - .sort(function(a,b){return a-b;});
990 + if(!rgb)return null;
921 921  
922 - if(
923 - lefts[lefts.length-1]-
924 - lefts[0]<
925 - 80
926 - ){
927 - return false;
928 - }
992 + var palette=[
993 + {hex:"#21b6d7",rgb:[33,182,215]},
994 + {hex:"#e94b13",rgb:[233,75,19]},
995 + {hex:"#f0aa00",rgb:[240,170,0]},
996 + {hex:"#38b76a",rgb:[56,183,106]}
997 + ];
929 929  
930 - var widths=
931 - group.entries
932 - .map(function(e){return e.rect.width;})
933 - .filter(function(w){return w>0;});
999 + var best=null;
1000 + var bestDistance=Infinity;
934 934  
935 - if(widths.length<2)return false;
1002 + palette.forEach(function(item){
1003 + var d=distance(rgb,item.rgb);
936 936  
937 - var minWidth=Math.min.apply(Math,widths);
938 - var maxWidth=Math.max.apply(Math,widths);
1005 + if(d<bestDistance){
1006 + best=item;
1007 + bestDistance=d;
1008 + }
1009 + });
939 939  
940 - return (
941 - maxWidth/
942 - Math.max(1,minWidth)<=
943 - 1.9
944 - );
1011 + return bestDistance<=30
1012 + ?best
1013 + :null;
945 945   }
946 946  
947 - function normalizeText(text){
948 - return String(text||"")
949 - .replace(/s+/g," ")
950 - .trim()
951 - .toUpperCase();
952 - }
1016 + function propagateDetailAccents(){
1017 + if(PAGE_KIND!=="detail")return;
953 953  
954 - function exactKnownFirstSection(el){
955 - var text=normalizeText(el.textContent);
956 -
957 - return (
958 - text==="HIER STARTEN"||
959 - text==="ORGANISIEREN & DOKUMENTIEREN"||
960 - text==="ENTSCHEIDEN"||
961 - text==="START HERE"||
962 - text==="GET STARTED"||
963 - text==="ORGANISE & DOCUMENT"||
964 - text==="ORGANIZE & DOCUMENT"||
965 - text==="DECIDE"
966 - );
967 - }
968 -
969 - function visuallyFirstSectionBar(card){
970 - var cardRect=card.getBoundingClientRect();
971 - var candidates=[];
972 -
973 973   Array.from(
974 - card.querySelectorAll(
975 - "div,p,span,strong,h3,h4,h5,h6"
1020 + root.querySelectorAll(
1021 + ".arsn-call,.dmp-call,.ido-call,.pp-call,"+
1022 + ".arsn-source,.dmp-source,.ido-source,.pp-source"
976 976   )
977 977   ).forEach(function(el){
978 - if(!visible(el,20,6))return;
979 - if(excluded(el))return;
1025 + if(!visible(el))return;
980 980  
981 - var r=el.getBoundingClientRect();
982 - var relativeTop=r.top-cardRect.top;
1027 + var style=getComputedStyle(el);
983 983  
984 984   if(
985 - relativeTop<45||
986 - relativeTop>cardRect.height*.72
1030 + num(style.borderTopWidth)>=2
987 987   ){
988 - return;
1032 + var match=
1033 + paletteMatch(
1034 + style.borderTopColor
1035 + );
1036 +
1037 + if(match){
1038 + el.style.setProperty(
1039 + "border-left",
1040 + "4px solid "+match.hex,
1041 + "important"
1042 + );
1043 + }
989 989   }
1045 + });
990 990  
991 - var widthRatio=
992 - r.width/
993 - Math.max(1,cardRect.width);
1047 + // Warning families: explicit safety override.
1048 + Array.from(
1049 + root.querySelectorAll(
1050 + ".arsn-warn,.dmp-warn,.ido-warn,.pp-warn,"+
1051 + ".warn,.warning,.warningmessage,.errormessage"
1052 + )
1053 + ).forEach(function(el){
1054 + if(!visible(el))return;
994 994  
995 - if(
996 - widthRatio<.55||
997 - widthRatio>1.05||
998 - r.height>54
999 - ){
1000 - return;
1001 - }
1056 + el.style.setProperty(
1057 + "border-left",
1058 + "4px solid #e94b13",
1059 + "important"
1060 + );
1061 + });
1002 1002  
1003 - var text=normalizeText(el.textContent);
1063 + // Info families: explicit orientation override.
1064 + Array.from(
1065 + root.querySelectorAll(
1066 + ".arsn-info,.dmp-info,.ido-info,.pp-info,.infomessage"
1067 + )
1068 + ).forEach(function(el){
1069 + if(!visible(el))return;
1004 1004  
1005 - if(
1006 - text.length<2||
1007 - text.length>90
1008 - ){
1009 - return;
1010 - }
1071 + el.style.setProperty(
1072 + "border-left",
1073 + "4px solid #21b6d7",
1074 + "important"
1075 + );
1076 + });
1077 + }
1011 1011  
1012 - var style=getComputedStyle(el);
1079 + /* =======================================================
1080 + OVERVIEW SEMANTIC THEME PALETTE
1081 + ======================================================= */
1013 1013  
1014 - var bg=
1015 - style.backgroundColor&&
1016 - style.backgroundColor!=="rgba(0, 0, 0, 0)"&&
1017 - style.backgroundColor!=="transparent";
1083 + function smallestThemeHeaders(){
1084 + return Array.from(
1085 + root.querySelectorAll(
1086 + "div,span,p,strong,h3,h4,h5,h6"
1087 + )
1088 + ).filter(function(el){
1089 + if(!visible(el))return false;
1090 + if(!themeFor(el.textContent))return false;
1018 1018  
1092 + return !Array.from(
1093 + el.children||[]
1094 + ).some(function(child){
1095 + return !!themeFor(
1096 + child.textContent
1097 + );
1098 + });
1099 + });
1100 + }
1101 +
1102 + function findOverviewCard(header){
1103 + var node=header.parentElement;
1104 + var candidates=[];
1105 +
1106 + while(node&&node!==root){
1107 + var rect=node.getBoundingClientRect();
1108 + var style=getComputedStyle(node);
1109 +
1019 1019   var border=
1020 1020   num(style.borderTopWidth)+
1021 1021   num(style.borderRightWidth)+
... ... @@ -1022,315 +1022,674 @@
1022 1022   num(style.borderBottomWidth)+
1023 1023   num(style.borderLeftWidth);
1024 1024  
1025 - var mostlyUpper=
1026 - text.length>=3&&
1027 - text===text.toUpperCase();
1028 -
1029 - var exact=exactKnownFirstSection(el);
1030 -
1031 - var score=
1032 - (exact?20:0)+
1033 - (bg?6:0)+
1034 - (border>=1?2:0)+
1035 - (mostlyUpper?4:0)+
1036 - (num(style.fontWeight)>=600?1:0);
1037 -
1038 - if(score>=7){
1039 - candidates.push({
1040 - el:el,
1041 - relativeTop:relativeTop,
1042 - width:r.width,
1043 - score:score
1044 - });
1116 + if(
1117 + rect.width>=220&&
1118 + rect.width<=800&&
1119 + rect.height>=120&&
1120 + border>=1
1121 + ){
1122 + candidates.push(node);
1045 1045   }
1046 - });
1047 1047  
1125 + node=node.parentElement;
1126 + }
1127 +
1048 1048   if(!candidates.length)return null;
1049 1049  
1050 1050   candidates.sort(function(a,b){
1051 - if(a.score!==b.score)return b.score-a.score;
1052 - if(Math.abs(a.relativeTop-b.relativeTop)>3){
1053 - return a.relativeTop-b.relativeTop;
1054 - }
1055 - return b.width-a.width;
1131 + return (
1132 + b.getBoundingClientRect().height-
1133 + a.getBoundingClientRect().height
1134 + );
1056 1056   });
1057 1057  
1058 - // For exact known labels always prefer the earliest exact match.
1059 - var exacts=candidates.filter(function(c){
1060 - return exactKnownFirstSection(c.el);
1061 - });
1137 + return candidates[0];
1138 + }
1062 1062  
1063 - if(exacts.length){
1064 - exacts.sort(function(a,b){
1065 - return a.relativeTop-b.relativeTop||b.width-a.width;
1066 - });
1067 - return exacts[0].el;
1068 - }
1140 + function rowCandidates(link,card){
1141 + var result=[];
1142 + var cardRect=
1143 + card.getBoundingClientRect();
1069 1069  
1070 - // Generic fallback: earliest high-confidence bar.
1071 - candidates.sort(function(a,b){
1072 - return a.relativeTop-b.relativeTop||b.score-a.score||b.width-a.width;
1073 - });
1145 + var node=link;
1074 1074  
1075 - return candidates[0].el;
1076 - }
1147 + while(node&&node!==card){
1148 + if(visible(node)){
1149 + var rect=
1150 + node.getBoundingClientRect();
1077 1077  
1078 - function alignFirstSections(cards){
1079 - var rows=
1080 - cards.map(function(card){
1081 - var bar=
1082 - visuallyFirstSectionBar(card);
1152 + if(
1153 + rect.height<=105&&
1154 + rect.width>=
1155 + cardRect.width*.50&&
1156 + rect.width<=
1157 + cardRect.width*1.03
1158 + ){
1159 + result.push(node);
1160 + }
1161 + }
1083 1083  
1084 - if(!bar)return null;
1163 + node=node.parentElement;
1164 + }
1085 1085  
1086 - var cardRect=
1087 - card.getBoundingClientRect();
1166 + return result;
1167 + }
1088 1088  
1089 - var barRect=
1090 - bar.getBoundingClientRect();
1169 + function chooseOuterRow(
1170 + candidates,
1171 + link
1172 + ){
1173 + if(!candidates.length){
1174 + return link;
1175 + }
1091 1091  
1092 - return {
1093 - card:card,
1094 - bar:bar,
1095 - relativeTop:
1096 - barRect.top-cardRect.top,
1097 - label:
1098 - normalizeText(bar.textContent)
1099 - };
1177 + var bordered=
1178 + candidates.filter(function(el){
1179 + return (
1180 + num(
1181 + getComputedStyle(el)
1182 + .borderLeftWidth
1183 + )>=1
1184 + );
1100 1100   });
1101 1101  
1102 - // Conservative: only align when every card in the horizontal row
1103 - // has a reliable first section bar.
1104 - if(
1105 - rows.some(function(row){
1106 - return !row;
1107 - })
1108 - ){
1109 - return null;
1187 + var pool=
1188 + bordered.length
1189 + ?bordered
1190 + :candidates;
1191 +
1192 + pool.sort(function(a,b){
1193 + var ar=
1194 + a.getBoundingClientRect();
1195 +
1196 + var br=
1197 + b.getBoundingClientRect();
1198 +
1199 + return (
1200 + ar.left-br.left||
1201 + br.width-ar.width
1202 + );
1203 + });
1204 +
1205 + return pool[0]||link;
1206 + }
1207 +
1208 + function applyOverviewPalette(){
1209 + if(PAGE_KIND!=="overview"){
1210 + return [];
1110 1110   }
1111 1111  
1112 - var targetTop=
1113 - Math.max.apply(
1114 - Math,
1115 - rows.map(function(row){
1116 - return row.relativeTop;
1213 + var headers=
1214 + smallestThemeHeaders()
1215 + .map(function(el){
1216 + return {
1217 + el:el,
1218 + theme:
1219 + themeFor(
1220 + el.textContent
1221 + ),
1222 + card:
1223 + findOverviewCard(
1224 + el
1225 + )
1226 + };
1117 1117   })
1118 - );
1228 + .filter(function(row){
1229 + return !!row.card;
1230 + });
1119 1119  
1120 - rows.forEach(function(row){
1121 - var delta=
1122 - targetTop-
1123 - row.relativeTop;
1232 + var unique=[];
1233 + var seenHeaders=new Set();
1124 1124  
1125 - if(delta<=1)return;
1235 + headers.forEach(function(row){
1236 + var key=
1237 + row.theme.name+
1238 + "|"+
1239 + Math.round(
1240 + row.card
1241 + .getBoundingClientRect()
1242 + .left
1243 + )+
1244 + "|"+
1245 + Math.round(
1246 + row.el
1247 + .getBoundingClientRect()
1248 + .top
1249 + );
1126 1126  
1127 - saveOriginal(
1128 - row.bar,
1129 - originalMarginTop,
1130 - "margin-top"
1251 + if(!seenHeaders.has(key)){
1252 + seenHeaders.add(key);
1253 + unique.push(row);
1254 + }
1255 + });
1256 +
1257 + unique.forEach(function(row){
1258 + var headerRect=
1259 + row.el
1260 + .getBoundingClientRect();
1261 +
1262 + row.el.style.setProperty(
1263 + "background",
1264 + row.theme.bg,
1265 + "important"
1131 1131   );
1132 1132  
1133 - var currentMargin=
1134 - num(
1135 - getComputedStyle(
1136 - row.bar
1137 - ).marginTop
1138 - );
1268 + row.el.style.setProperty(
1269 + "color",
1270 + row.theme.text,
1271 + "important"
1272 + );
1139 1139  
1140 - row.bar.style.setProperty(
1141 - "margin-top",
1142 - (
1143 - currentMargin+
1144 - delta
1145 - )+"px"
1274 + row.el.style.setProperty(
1275 + "text-align",
1276 + "left",
1277 + "important"
1146 1146   );
1147 1147  
1148 - row.bar.setAttribute(
1149 - "data-sn-first-section-aligned",
1150 - "4"
1280 + row.el.style.setProperty(
1281 + "word-spacing",
1282 + "normal",
1283 + "important"
1151 1151   );
1152 1152  
1153 - sectionManaged.add(row.bar);
1286 + var later=
1287 + unique
1288 + .filter(function(other){
1289 + return (
1290 + other.card===row.card&&
1291 + other.el
1292 + .getBoundingClientRect()
1293 + .top>
1294 + headerRect.top+2
1295 + );
1296 + })
1297 + .sort(function(a,b){
1298 + return (
1299 + a.el
1300 + .getBoundingClientRect()
1301 + .top-
1302 + b.el
1303 + .getBoundingClientRect()
1304 + .top
1305 + );
1306 + });
1307 +
1308 + var stopTop=
1309 + later.length
1310 + ?later[0].el
1311 + .getBoundingClientRect()
1312 + .top
1313 + :Infinity;
1314 +
1315 + Array.from(
1316 + row.card.querySelectorAll("a")
1317 + ).forEach(function(link){
1318 + if(!visible(link))return;
1319 +
1320 + var rect=
1321 + link.getBoundingClientRect();
1322 +
1323 + if(
1324 + rect.top<=
1325 + headerRect.bottom-2||
1326 + rect.top>=
1327 + stopTop-2
1328 + ){
1329 + return;
1330 + }
1331 +
1332 + var candidates=
1333 + rowCandidates(
1334 + link,
1335 + row.card
1336 + );
1337 +
1338 + var target=
1339 + chooseOuterRow(
1340 + candidates,
1341 + link
1342 + );
1343 +
1344 + candidates.forEach(function(el){
1345 + el.style.setProperty(
1346 + "border-left",
1347 + "0 solid transparent",
1348 + "important"
1349 + );
1350 + });
1351 +
1352 + target.style.setProperty(
1353 + "border-left",
1354 + "3px solid "+
1355 + row.theme.accent,
1356 + "important"
1357 + );
1358 +
1359 + target.style.setProperty(
1360 + "box-sizing",
1361 + "border-box",
1362 + "important"
1363 + );
1364 + });
1154 1154   });
1155 1155  
1156 - return {
1157 - labels:
1158 - rows.map(function(row){
1159 - return row.label;
1160 - }),
1161 - targetRelativeTop:
1162 - Math.round(targetTop)
1163 - };
1367 + return unique;
1164 1164   }
1165 1165  
1166 - function applyGroup(group){
1167 - if(!validHorizontalGroup(group)){
1168 - return null;
1370 + /* =======================================================
1371 + OVERVIEW CARD HEIGHT + FIRST THEMATIC ROW
1372 + ======================================================= */
1373 +
1374 + function alignOverviewCards(headers){
1375 + if(
1376 + PAGE_KIND!=="overview"||
1377 + window.innerWidth<=MOBILE_MAX
1378 + ){
1379 + return;
1169 1169   }
1170 1170  
1171 1171   var cards=[];
1172 1172   var seen=new Set();
1173 1173  
1174 - group.entries.forEach(function(entry){
1175 - if(!seen.has(entry.target)){
1176 - seen.add(entry.target);
1177 - cards.push(entry.target);
1385 + headers.forEach(function(row){
1386 + if(!seen.has(row.card)){
1387 + seen.add(row.card);
1388 + cards.push(row.card);
1178 1178   }
1179 1179   });
1180 1180  
1181 - if(cards.length<2)return null;
1392 + if(cards.length<2){
1393 + return;
1394 + }
1182 1182  
1183 - var firstSection=
1184 - alignFirstSections(cards);
1396 + // Group cards by actual top position.
1397 + var groups=[];
1185 1185  
1186 - // The section alignment can change natural card heights.
1187 - // Measure only afterwards.
1188 - var maxHeight=0;
1189 -
1190 1190   cards.forEach(function(card){
1191 - maxHeight=Math.max(
1192 - maxHeight,
1193 - card.getBoundingClientRect().height
1194 - );
1400 + var top=
1401 + card.getBoundingClientRect().top;
1402 +
1403 + var group=null;
1404 +
1405 + for(var i=0;i<groups.length;i++){
1406 + if(
1407 + Math.abs(
1408 + groups[i].top-top
1409 + )<=ROW_TOLERANCE
1410 + ){
1411 + group=groups[i];
1412 + break;
1413 + }
1414 + }
1415 +
1416 + if(!group){
1417 + group={
1418 + top:top,
1419 + cards:[]
1420 + };
1421 + groups.push(group);
1422 + }
1423 +
1424 + group.cards.push(card);
1195 1195   });
1196 1196  
1197 - maxHeight=Math.ceil(maxHeight);
1427 + groups.forEach(function(group){
1428 + if(group.cards.length<2){
1429 + return;
1430 + }
1198 1198  
1199 - if(maxHeight<MIN_H)return null;
1432 + var firstHeaders=
1433 + group.cards.map(function(card){
1434 + var candidates=
1435 + headers
1436 + .filter(function(row){
1437 + return row.card===card;
1438 + })
1439 + .sort(function(a,b){
1440 + return (
1441 + a.el
1442 + .getBoundingClientRect()
1443 + .top-
1444 + b.el
1445 + .getBoundingClientRect()
1446 + .top
1447 + );
1448 + });
1200 1200  
1201 - cards.forEach(function(card){
1202 - saveOriginal(
1203 - card,
1204 - originalMinHeight,
1205 - "min-height"
1206 - );
1450 + return candidates.length
1451 + ?candidates[0]
1452 + :null;
1453 + });
1207 1207  
1208 - card.style.setProperty(
1209 - "min-height",
1210 - maxHeight+"px"
1211 - );
1455 + if(
1456 + !firstHeaders.some(function(row){
1457 + return !row;
1458 + })
1459 + ){
1460 + var targetTop=
1461 + Math.max.apply(
1462 + Math,
1463 + firstHeaders.map(function(row){
1464 + return (
1465 + row.el
1466 + .getBoundingClientRect()
1467 + .top-
1468 + row.card
1469 + .getBoundingClientRect()
1470 + .top
1471 + );
1472 + })
1473 + );
1212 1212  
1213 - card.setAttribute(
1214 - "data-sn-equal-height-managed",
1215 - "4"
1216 - );
1475 + firstHeaders.forEach(function(row){
1476 + var relativeTop=
1477 + row.el
1478 + .getBoundingClientRect()
1479 + .top-
1480 + row.card
1481 + .getBoundingClientRect()
1482 + .top;
1217 1217  
1218 - heightManaged.add(card);
1219 - });
1484 + var delta=
1485 + targetTop-relativeTop;
1220 1220  
1221 - return {
1222 - cards:cards.length,
1223 - height:maxHeight,
1224 - firstSection:firstSection
1225 - };
1487 + if(delta>1){
1488 + saveStyle(
1489 + row.el,
1490 + originalMargin,
1491 + "margin-top"
1492 + );
1493 +
1494 + var current=
1495 + num(
1496 + getComputedStyle(
1497 + row.el
1498 + ).marginTop
1499 + );
1500 +
1501 + row.el.style.setProperty(
1502 + "margin-top",
1503 + (
1504 + current+delta
1505 + )+"px"
1506 + );
1507 +
1508 + row.el.setAttribute(
1509 + "data-sn-master-first-row",
1510 + "1"
1511 + );
1512 +
1513 + managedMargin.add(
1514 + row.el
1515 + );
1516 + }
1517 + });
1518 + }
1519 +
1520 + var maxHeight=0;
1521 +
1522 + group.cards.forEach(function(card){
1523 + maxHeight=
1524 + Math.max(
1525 + maxHeight,
1526 + card
1527 + .getBoundingClientRect()
1528 + .height
1529 + );
1530 + });
1531 +
1532 + maxHeight=
1533 + Math.ceil(maxHeight);
1534 +
1535 + group.cards.forEach(function(card){
1536 + saveStyle(
1537 + card,
1538 + originalHeight,
1539 + "min-height"
1540 + );
1541 +
1542 + card.style.setProperty(
1543 + "min-height",
1544 + maxHeight+"px"
1545 + );
1546 +
1547 + card.setAttribute(
1548 + "data-sn-master-equal-height",
1549 + "1"
1550 + );
1551 +
1552 + managedHeight.add(
1553 + card
1554 + );
1555 + });
1556 + });
1226 1226   }
1227 1227  
1228 - function run(){
1229 - reset();
1559 + /* =======================================================
1560 + DETAIL GRID EQUAL HEIGHT, DESKTOP ONLY
1561 + ======================================================= */
1230 1230  
1563 + function equaliseDetailGrids(){
1231 1231   if(
1565 + PAGE_KIND!=="detail"||
1232 1232   window.innerWidth<=MOBILE_MAX
1233 1233   ){
1234 1234   return;
1235 1235   }
1236 1236  
1237 - var parents=
1571 + var selectors=[
1572 + ".arsn-grid",
1573 + ".arsn-grid-2",
1574 + ".arsn-grid-3",
1575 + ".dmp-grid",
1576 + ".ido-grid",
1577 + ".pp-grid"
1578 + ];
1579 +
1580 + selectors.forEach(function(selector){
1238 1238   Array.from(
1239 - root.querySelectorAll("*")
1240 - ).filter(function(parent){
1241 - return (
1242 - visible(parent,MIN_W,MIN_H)&&
1243 - parent.children&&
1244 - parent.children.length>=2&&
1245 - parent.children.length<=MAX_PARENT_CHILDREN
1246 - );
1247 - });
1582 + root.querySelectorAll(selector)
1583 + ).forEach(function(grid){
1584 + var children=
1585 + Array.from(
1586 + grid.children||[]
1587 + ).filter(visible);
1248 1248  
1249 - var touched=new Set();
1589 + if(children.length<2){
1590 + return;
1591 + }
1250 1250  
1251 - parents.forEach(function(parent){
1252 - var entries=[];
1593 + var groups=[];
1253 1253  
1254 - Array.from(parent.children).forEach(function(child){
1255 - if(!visible(child,MIN_W,MIN_H))return;
1595 + children.forEach(function(child){
1596 + var top=
1597 + child
1598 + .getBoundingClientRect()
1599 + .top;
1256 1256  
1257 - var target=pickTarget(child);
1258 - if(!target)return;
1601 + var group=null;
1259 1259  
1260 - entries.push({
1261 - target:target,
1262 - rect:target.getBoundingClientRect()
1603 + for(var i=0;i<groups.length;i++){
1604 + if(
1605 + Math.abs(
1606 + groups[i].top-top
1607 + )<=ROW_TOLERANCE
1608 + ){
1609 + group=groups[i];
1610 + break;
1611 + }
1612 + }
1613 +
1614 + if(!group){
1615 + group={
1616 + top:top,
1617 + items:[]
1618 + };
1619 + groups.push(group);
1620 + }
1621 +
1622 + group.items.push(child);
1263 1263   });
1264 - });
1265 1265  
1266 - if(entries.length<2)return;
1625 + groups.forEach(function(group){
1626 + if(group.items.length<2){
1627 + return;
1628 + }
1267 1267  
1268 - groupsByVisualRow(entries).forEach(function(group){
1269 - if(!validHorizontalGroup(group))return;
1630 + var maxHeight=0;
1270 1270  
1271 - var signature=
1272 - group.entries.map(function(entry){
1273 - return entry.target;
1632 + group.items.forEach(function(item){
1633 + maxHeight=
1634 + Math.max(
1635 + maxHeight,
1636 + item
1637 + .getBoundingClientRect()
1638 + .height
1639 + );
1274 1274   });
1275 1275  
1276 - if(
1277 - !signature.some(function(el){
1278 - return !touched.has(el);
1279 - })
1280 - ){
1281 - return;
1282 - }
1642 + maxHeight=
1643 + Math.ceil(maxHeight);
1283 1283  
1284 - var applied=
1285 - applyGroup(group);
1645 + group.items.forEach(function(item){
1646 + saveStyle(
1647 + item,
1648 + originalHeight,
1649 + "min-height"
1650 + );
1286 1286  
1287 - if(applied){
1288 - signature.forEach(function(el){
1289 - touched.add(el);
1652 + item.style.setProperty(
1653 + "min-height",
1654 + maxHeight+"px"
1655 + );
1656 +
1657 + item.setAttribute(
1658 + "data-sn-master-equal-height",
1659 + "1"
1660 + );
1661 +
1662 + managedHeight.add(item);
1290 1290   });
1291 - }
1664 + });
1292 1292   });
1293 1293   });
1294 1294   }
1295 1295  
1296 - function schedule(){
1297 - if(raf)cancelAnimationFrame(raf);
1669 + /* =======================================================
1670 + RUN / RESPONSIVE SCHEDULING
1671 + ======================================================= */
1298 1298  
1299 - raf=requestAnimationFrame(function(){
1300 - raf=requestAnimationFrame(function(){
1301 - raf=0;
1302 - run();
1303 - });
1304 - });
1673 + function run(){
1674 + if(running)return;
1675 +
1676 + running=true;
1677 +
1678 + try{
1679 + resetRuntime();
1680 + applyPlaceholderBadge();
1681 + propagateDetailAccents();
1682 +
1683 + var headers=
1684 + applyOverviewPalette();
1685 +
1686 + if(
1687 + window.innerWidth>
1688 + MOBILE_MAX
1689 + ){
1690 + alignOverviewCards(
1691 + headers
1692 + );
1693 +
1694 + equaliseDetailGrids();
1695 + }
1696 + }finally{
1697 + running=false;
1698 + }
1305 1305   }
1306 1306  
1307 - addEventListener("resize",schedule,{passive:true});
1308 - addEventListener("load",schedule,{once:true});
1701 + function onResize(){
1702 + if(resizeTimer){
1703 + clearTimeout(
1704 + resizeTimer
1705 + );
1706 + }
1309 1309  
1310 - root.addEventListener(
1311 - "click",
1312 - function(){
1313 - setTimeout(schedule,0);
1314 - },
1315 - true
1316 - );
1708 + resizeTimer=
1709 + setTimeout(
1710 + function(){
1711 + resizeTimer=0;
1712 + run();
1713 + },
1714 + RESIZE_DEBOUNCE
1715 + );
1716 + }
1317 1317  
1318 - Array.from(root.querySelectorAll("img")).forEach(function(img){
1319 - if(!img.complete){
1320 - img.addEventListener("load",schedule,{once:true});
1718 + async function waitForReady(){
1719 + if(
1720 + document.readyState!=="complete"
1721 + ){
1722 + await new Promise(function(resolve){
1723 + window.addEventListener(
1724 + "load",
1725 + resolve,
1726 + {once:true}
1727 + );
1728 + });
1321 1321   }
1322 - });
1323 1323  
1324 - if(document.fonts&&document.fonts.ready){
1325 - document.fonts.ready.then(schedule);
1731 + if(
1732 + document.fonts&&
1733 + document.fonts.ready
1734 + ){
1735 + try{
1736 + await document.fonts.ready;
1737 + }catch(error){}
1738 + }
1739 +
1740 + var images=
1741 + Array.from(
1742 + root.querySelectorAll("img")
1743 + );
1744 +
1745 + await Promise.all(
1746 + images.map(function(img){
1747 + if(img.complete){
1748 + return Promise.resolve();
1749 + }
1750 +
1751 + return new Promise(function(resolve){
1752 + var done=function(){
1753 + resolve();
1754 + };
1755 +
1756 + img.addEventListener(
1757 + "load",
1758 + done,
1759 + {once:true}
1760 + );
1761 +
1762 + img.addEventListener(
1763 + "error",
1764 + done,
1765 + {once:true}
1766 + );
1767 + });
1768 + })
1769 + );
1770 +
1771 + run();
1326 1326   }
1327 1327  
1328 - schedule();
1329 - setTimeout(schedule,200);
1330 - setTimeout(schedule,650);
1331 - setTimeout(schedule,1400);
1774 + window.addEventListener(
1775 + "resize",
1776 + onResize,
1777 + {passive:true}
1778 + );
1779 +
1780 + waitForReady();
1332 1332  })();
1333 1333  </script>
1334 -<!-- SN_HORIZONTAL_BOX_HARMONIZER_END -->
1783 +
1784 +<!-- SN_SERVICE_NAVIGATOR_MASTER_STYLE_END -->
1335 1335  {{/html}}
1336 1336