liquid_feedback_frontend
changeset 598:9df26b41ace0
Improved member home page and area list
author | bsw |
---|---|
date | Sun Jun 24 22:30:29 2012 +0200 (2012-06-24) |
parents | 516962033e96 |
children | 5dc5f923797b |
files | app/main/area/_list.lua app/main/area/_list_entry.lua app/main/index/_member_home.lua static/style.css |
line diff
1.1 --- a/app/main/area/_list.lua Sun Jun 24 20:50:02 2012 +0200 1.2 +++ b/app/main/area/_list.lua Sun Jun 24 22:30:29 2012 +0200 1.3 @@ -29,176 +29,13 @@ 1.4 areas_selector:add_field("0", "issues_to_vote_count") 1.5 end 1.6 1.7 - 1.8 -ui.container{ attr = { class = "box area_list" }, content = function() 1.9 - 1.10 - ui.container{ attr = { class = "area head" }, content = function() 1.11 - 1.12 - ui.container{ attr = { class = "phases" }, content = function() 1.13 - 1.14 - ui.container{ attr = { class = "admission" }, content = function() 1.15 - ui.image{ static = "icons/16/new.png", attr = { alt = _"New" } } 1.16 - end } 1.17 - 1.18 - ui.container{ attr = { class = "discussion" }, content = function() 1.19 - ui.image{ static = "icons/16/comments.png", attr = { alt = _"Discussion" } } 1.20 - end } 1.21 - 1.22 - ui.container{ attr = { class = "verification" }, content = function() 1.23 - ui.image{ static = "icons/16/lock.png", attr = { alt = _"Verification" } } 1.24 - end } 1.25 - 1.26 - ui.container{ attr = { class = "voting" }, content = function() 1.27 - ui.image{ static = "icons/16/email_open.png", attr = { alt = _"Voting" } } 1.28 - end } 1.29 - 1.30 - ui.container{ attr = { class = "finished" }, content = function() 1.31 - ui.image{ static = "icons/16/tick.png", attr = { alt = _"Finished" } } 1.32 - end } 1.33 - 1.34 - ui.container{ attr = { class = "cancelled" }, content = function() 1.35 - ui.image{ static = "icons/16/cross.png", attr = { alt = _"Cancelled" } } 1.36 - end } 1.37 - 1.38 - end } 1.39 - 1.40 - end } 1.41 - 1.42 - for i, area in ipairs(areas_selector:exec()) do 1.43 - 1.44 - ui.container{ attr = { class = "area" }, content = function() 1.45 - 1.46 - ui.container{ attr = { class = "bar" }, content = function() 1.47 - if area.member_weight and area.direct_member_count then 1.48 - local max_value = MemberCount:get() 1.49 - ui.bargraph{ 1.50 - max_value = max_value, 1.51 - width = 100, 1.52 - bars = { 1.53 - { color = "#444", value = area.direct_member_count }, 1.54 - { color = "#777", value = area.member_weight - area.direct_member_count }, 1.55 - { color = "#ddd", value = max_value - area.member_weight }, 1.56 - } 1.57 - } 1.58 - end 1.59 - end } 1.60 - 1.61 - ui.container{ attr = { class = "name" }, content = function() 1.62 - ui.link{ 1.63 - text = area.name, 1.64 - module = "area", 1.65 - view = "show", 1.66 - id = area.id 1.67 - } 1.68 - slot.put(" ") 1.69 - ui.tag{ content = "" } 1.70 - end } 1.71 +slot.put("<br />") 1.72 1.73 - if not hide_membership then 1.74 - ui.container{ attr = { class = "membership" }, content = function() 1.75 - if area.is_member then 1.76 - local text = _"Member of area" 1.77 - ui.image{ 1.78 - attr = { title = text, alt = text }, 1.79 - static = "icons/16/user_gray.png", 1.80 - } 1.81 - else 1.82 - slot.put('<img src="null.png" width="16" height="1" alt="" />') 1.83 - end 1.84 - end } 1.85 - end 1.86 - 1.87 - ui.container{ attr = { class = "delegatee" }, content = function() 1.88 - if area.trustee_member_id then 1.89 - local trustee_member = Member:by_id(area.trustee_member_id) 1.90 - local text = _("Area delegated to '#{name}'", { name = area.trustee_member_name }) 1.91 - ui.image{ 1.92 - attr = { class = "delegation_arrow", alt = text, title = text }, 1.93 - static = "delegation_arrow_24_horizontal.png" 1.94 - } 1.95 - execute.view{ 1.96 - module = "member_image", 1.97 - view = "_show", 1.98 - params = { 1.99 - member = trustee_member, 1.100 - image_type = "avatar", 1.101 - show_dummy = true, 1.102 - class = "micro_avatar", 1.103 - popup_text = text 1.104 - } 1.105 - } 1.106 - else 1.107 - slot.put('<img src="null.png" width="41" height="1" alt="" />') 1.108 - end 1.109 - end } 1.110 +for i, area in ipairs(areas_selector:exec()) do 1.111 1.112 - ui.container{ attr = { class = "phases" }, content = function() 1.113 - 1.114 - ui.container{ attr = { class = "admission" }, content = function() 1.115 - ui.link{ 1.116 - text = tostring(area.issues_new_count), 1.117 - module = "area", 1.118 - view = "show", 1.119 - id = area.id, 1.120 - params = { filter = "new", tab = "open" } 1.121 - } 1.122 - end } 1.123 + ui.container{ attr = { class = "area_list" }, content = function() 1.124 + execute.view { module = "area", view = "_list_entry", params = { area = area } } 1.125 + end } 1.126 + 1.127 +end 1.128 1.129 - ui.container{ attr = { class = "discussion" }, content = function() 1.130 - ui.link{ 1.131 - text = tostring(area.issues_discussion_count), 1.132 - module = "area", 1.133 - view = "show", 1.134 - id = area.id, 1.135 - params = { filter = "accepted", tab = "open" } 1.136 - } 1.137 - end } 1.138 - 1.139 - ui.container{ attr = { class = "verification" }, content = function() 1.140 - ui.link{ 1.141 - text = tostring(area.issues_frozen_count), 1.142 - module = "area", 1.143 - view = "show", 1.144 - id = area.id, 1.145 - params = { filter = "half_frozen", tab = "open" } 1.146 - } 1.147 - end } 1.148 - 1.149 - ui.container{ attr = { class = "voting" }, content = function() 1.150 - ui.link{ 1.151 - text = tostring(area.issues_voting_count), 1.152 - module = "area", 1.153 - view = "show", 1.154 - id = area.id, 1.155 - params = { filter = "frozen", tab = "open" } 1.156 - } 1.157 - end } 1.158 - 1.159 - ui.container{ attr = { class = "finished" }, content = function() 1.160 - ui.link{ 1.161 - text = tostring(area.issues_finished_count), 1.162 - module = "area", 1.163 - view = "show", 1.164 - id = area.id, 1.165 - params = { filter = "finished", tab = "closed" } 1.166 - } 1.167 - end } 1.168 - 1.169 - ui.container{ attr = { class = "cancelled" }, content = function() 1.170 - ui.link{ 1.171 - text = tostring(area.issues_cancelled_count), 1.172 - module = "area", 1.173 - view = "show", 1.174 - id = area.id, 1.175 - params = { filter = "cancelled", issue_list = "newest", tab = "closed" } 1.176 - } 1.177 - end } 1.178 - 1.179 - end } 1.180 - 1.181 - slot.put("<br style='clear: right;' />") 1.182 - end } 1.183 - 1.184 - end 1.185 - 1.186 -end }
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/app/main/area/_list_entry.lua Sun Jun 24 22:30:29 2012 +0200 2.3 @@ -0,0 +1,46 @@ 2.4 +local area = param.get("area", "table") 2.5 + 2.6 +ui.container{ attr = { class = "area" }, content = function() 2.7 + execute.view{ module = "delegation", view = "_info", params = { area = area } } 2.8 + 2.9 + ui.container{ attr = { class = "title" }, content = function() 2.10 + -- area name 2.11 + ui.link{ 2.12 + module = "area", view = "show", id = area.id, 2.13 + attr = { class = "area_name" }, content = area.name 2.14 + } 2.15 + end } 2.16 + 2.17 + ui.container{ attr = { class = "content" }, content = function() 2.18 + ui.link{ 2.19 + module = "area", view = "show", id = area.id, params = { tab = "open", filter = "new" }, 2.20 + text = _("#{count} new", { count = area.issues_new_count }) 2.21 + } 2.22 + slot.put(" · ") 2.23 + ui.link{ 2.24 + module = "area", view = "show", id = area.id, params = { tab = "open", filter = "discussion" }, 2.25 + text = _("#{count} in discussion", { count = area.issues_discussion_count }) 2.26 + } 2.27 + slot.put(" · ") 2.28 + ui.link{ 2.29 + module = "area", view = "show", id = area.id, params = { tab = "open", filter = "verification" }, 2.30 + text = _("#{count} in verification", { count = area.issues_frozen_count }) 2.31 + } 2.32 + slot.put(" · ") 2.33 + ui.link{ 2.34 + module = "area", view = "show", id = area.id, params = { tab = "open", filter = "voting" }, 2.35 + text = _("#{count} in voting", { count = area.issues_voting_count }) 2.36 + } 2.37 + slot.put(" · ") 2.38 + ui.link{ 2.39 + module = "area", view = "show", id = area.id, params = { tab = "closed", filter = "finished" }, 2.40 + text = _("#{count} finished", { count = area.issues_finished_count }) 2.41 + } 2.42 + slot.put(" · ") 2.43 + ui.link{ 2.44 + module = "area", view = "show", id = area.id, params = { tab = "closed", filter = "cancelled" }, 2.45 + text = _("#{count} cancelled", { count = area.issues_cancelled_count }) 2.46 + } 2.47 + end } 2.48 + 2.49 +end }
3.1 --- a/app/main/index/_member_home.lua Sun Jun 24 20:50:02 2012 +0200 3.2 +++ b/app/main/index/_member_home.lua Sun Jun 24 22:30:29 2012 +0200 3.3 @@ -1,27 +1,37 @@ 3.4 local member = param.get("member", "table") 3.5 local for_member = param.get("for_member", atom.boolean) 3.6 -local filter_unit = param.get_all_cgi()["filter_unit"] or "personal" 3.7 +local filter_unit = param.get_all_cgi()["filter_unit"] or "my_areas" 3.8 3.9 if not for_member then 3.10 execute.view{ 3.11 module = "index", view = "_notifications" 3.12 } 3.13 3.14 - ui.container{ attr = { class = "ui_filter_head" }, content = function() 3.15 + ui.container{ attr = { class = "ui_filter" }, content = function() 3.16 + ui.container{ attr = { class = "ui_filter_head" }, content = function() 3.17 + 3.18 + ui.link{ 3.19 + attr = { class = filter_unit == "my_areas" and "ui_tabs_link active" or nil }, 3.20 + text = _"My areas", 3.21 + module = "index", view = "index", params = { filter_unit = "my_areas" } 3.22 + } 3.23 + 3.24 + slot.put(" ") 3.25 3.26 - ui.link{ 3.27 - attr = { class = filter_unit == "personal" and "ui_tabs_link active" or nil }, 3.28 - text = _"My units and areas", 3.29 - module = "index", view = "index", params = { filter_unit = "personal" } 3.30 - } 3.31 - 3.32 - slot.put(" ") 3.33 + ui.link{ 3.34 + attr = { class = filter_unit == "my_units" and "ui_tabs_link active" or nil }, 3.35 + text = _"All areas in my units", 3.36 + module = "index", view = "index", params = { filter_unit = "my_units" } 3.37 + } 3.38 + 3.39 + slot.put(" ") 3.40 3.41 - ui.link{ 3.42 - attr = { class = filter_unit == "global" and "active" or nil }, 3.43 - text = _"All units", 3.44 - module = "index", view = "index", params = { filter_unit = "global" } 3.45 - } 3.46 + ui.link{ 3.47 + attr = { class = filter_unit == "global" and "active" or nil }, 3.48 + text = _"All units", 3.49 + module = "index", view = "index", params = { filter_unit = "global" } 3.50 + } 3.51 + end } 3.52 end } 3.53 end 3.54 3.55 @@ -35,61 +45,67 @@ 3.56 3.57 end 3.58 3.59 -local units = Unit:new_selector():exec() 3.60 +local units = Unit:new_selector():add_order_by("name"):exec() 3.61 + 3.62 +if member then 3.63 + units:load_delegation_info_once_for_member_id(member.id) 3.64 +end 3.65 3.66 for i, unit in ipairs(units) do 3.67 if member:has_voting_right_for_unit_id(unit.id) then 3.68 - local trustee_member = Member:new_selector() 3.69 - :join("delegation", nil, { "delegation.scope = 'unit' AND delegation.unit_id = ? AND delegation.trustee_id = member.id AND delegation.truster_id = ?", unit.id, member.id }) 3.70 - :optional_object_mode() 3.71 - :exec() 3.72 - 3.73 + 3.74 local areas_selector = Area:new_selector() 3.75 - :join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ?", member.id }) 3.76 + :reset_fields() 3.77 + :add_field("area.id", nil, { "grouped" }) 3.78 + :add_field("area.name", nil, { "grouped" }) 3.79 + :add_field("member_weight", nil, { "grouped" }) 3.80 + :add_field("direct_member_count", nil, { "grouped" }) 3.81 + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.accepted ISNULL AND issue.closed ISNULL)", "issues_new_count") 3.82 + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.accepted NOTNULL AND issue.half_frozen ISNULL AND issue.closed ISNULL)", "issues_discussion_count") 3.83 + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.half_frozen NOTNULL AND issue.fully_frozen ISNULL AND issue.closed ISNULL)", "issues_frozen_count") 3.84 + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed ISNULL)", "issues_voting_count") 3.85 + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed NOTNULL)", "issues_finished_count") 3.86 + :add_field("(SELECT COUNT(*) FROM issue WHERE issue.area_id = area.id AND issue.fully_frozen ISNULL AND issue.closed NOTNULL)", "issues_cancelled_count") 3.87 :add_where{ "area.unit_id = ?", unit.id } 3.88 :add_where{ "area.active" } 3.89 - :add_order_by("area.member_weight DESC") 3.90 + :add_order_by("area.name") 3.91 + 3.92 + if filter_unit == "my_areas" then 3.93 + areas_selector:join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ?", member.id }) 3.94 + end 3.95 3.96 local area_count = areas_selector:count() 3.97 3.98 - ui.container{ attr = { class = "member_area_list" }, content = function() 3.99 + ui.container{ attr = { class = "area_list" }, content = function() 3.100 ui.container{ attr = { class = "unit_head" }, content = function() 3.101 ui.link{ 3.102 text = unit.name, 3.103 module = "unit", view = "show", id = unit.id 3.104 } 3.105 3.106 - if trustee_member then 3.107 - local text = _("Unit delegated to '#{name}'", { name = trustee_member.name }) 3.108 - ui.image{ 3.109 - attr = { class = "delegation_arrow", alt = text, title = text }, 3.110 - static = "delegation_arrow_24_horizontal.png" 3.111 - } 3.112 + execute.view{ module = "delegation", view = "_info", params = { unit = unit } } 3.113 + end } 3.114 + 3.115 + if area_count > 0 then 3.116 + local areas = areas_selector:exec() 3.117 + for i, area in ipairs(areas) do 3.118 execute.view{ 3.119 - module = "member_image", 3.120 - view = "_show", 3.121 - params = { 3.122 - member = trustee_member, 3.123 - image_type = "avatar", 3.124 - show_dummy = true, 3.125 - class = "micro_avatar", 3.126 - popup_text = text 3.127 + module = "area", view = "_list_entry", params = { 3.128 + area = area 3.129 } 3.130 } 3.131 end 3.132 - end } 3.133 - 3.134 - if area_count > 0 then 3.135 - execute.view{ 3.136 - module = "area", view = "_list", 3.137 - params = { areas_selector = areas_selector, hide_membership = true } 3.138 - } 3.139 elseif member:has_voting_right_for_unit_id(unit.id) then 3.140 - if for_member then 3.141 - ui.container{ attr = { class = "voting_priv_info" }, content = _"This member has voting privileges for this unit, but you ist not member of any of its areas." } 3.142 - else 3.143 - ui.container{ attr = { class = "voting_priv_info" }, content = _"You have voting privileges for this unit, but you are not member of any of its areas." } 3.144 - end 3.145 + ui.container{ attr = { class = "area" }, content = function() 3.146 + ui.container{ attr = { class = "content" }, content = function() 3.147 + slot.put("<br />") 3.148 + if for_member then 3.149 + ui.tag{ content = _"This member has voting privileges for this unit, but you ist not member of any of its areas." } 3.150 + else 3.151 + ui.tag{ content = _"You have voting privileges for this unit, but you are not member of any of its areas." } 3.152 + end 3.153 + end } 3.154 + end } 3.155 end 3.156 local max_area_count = Area:new_selector() 3.157 :add_where{ "area.unit_id = ?", unit.id } 3.158 @@ -102,13 +118,18 @@ 3.159 :left_join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ?", member.id } ) 3.160 :add_where{ "membership.member_id ISNULL" } 3.161 :join("delegation", nil, { "delegation.area_id = area.id AND delegation.truster_id = ?", member.id } ) 3.162 + :add_where{ "delegation.trustee_id NOTNULL" } 3.163 :count() 3.164 if more_area_count > 0 then 3.165 - slot.put("<br />") 3.166 - ui.container{ attr = { class = "more_areas" }, content = function() 3.167 - ui.link{ content = _("#{count} more areas in this unit, #{delegated_count} of them are delegated", { count = more_area_count, delegated_count = delegated_count }), module = "unit", view = "show", id = unit.id } 3.168 + ui.container{ attr = { class = "area" }, content = function() 3.169 + ui.container{ attr = { class = "content" }, content = function() 3.170 + slot.put("<br />") 3.171 + ui.link{ content = _("#{count} more areas in this unit, #{delegated_count} of them have an area delegation set", { count = more_area_count, delegated_count = delegated_count }), module = "unit", view = "show", id = unit.id } 3.172 + end } 3.173 end } 3.174 end 3.175 + slot.put("<br />") 3.176 + slot.put("<br />") 3.177 end } 3.178 end 3.179 end
4.1 --- a/static/style.css Sun Jun 24 20:50:02 2012 +0200 4.2 +++ b/static/style.css Sun Jun 24 22:30:29 2012 +0200 4.3 @@ -359,14 +359,16 @@ 4.4 .page_head .title, 4.5 .issue .context, 4.6 .issue .title, 4.7 -.initiative_head .title { 4.8 +.initiative_head .title, 4.9 +.area_list .title { 4.10 padding: 8px 10px 2px 10px; 4.11 } 4.12 4.13 .page_head .unit_head .content, 4.14 .page_head .area_head .content, 4.15 .issue .content, 4.16 -.initiative_head .content { 4.17 +.initiative_head .content, 4.18 +.area_list .area .content { 4.19 padding: 2px 10px 8px 10px; 4.20 } 4.21 4.22 @@ -383,7 +385,8 @@ 4.23 4.24 .page_head .title, 4.25 .issue .title, 4.26 -.initiative_head .title { 4.27 +.initiative_head .title, 4.28 +.area_list .area_name { 4.29 font-weight: bold; 4.30 font-size: 120%; 4.31 } 4.32 @@ -401,7 +404,9 @@ 4.33 .page_head .issue .content.actions, 4.34 .page_head .issue .content.actions a, 4.35 .issues .issue .content.actions, 4.36 -.issues .issue .content.actions a { 4.37 +.issues .issue .content.actions a, 4.38 +.area_list .area .content, 4.39 +.area_list .area .content a { 4.40 color: #777; 4.41 } 4.42 4.43 @@ -775,104 +780,17 @@ 4.44 * Area list 4.45 */ 4.46 4.47 -.member_area_list { 4.48 - margin-bottom: 0ex; 4.49 - border-radius: 8px; 4.50 - border: 1px solid #aaa; 4.51 - padding: 1ex; 4.52 - margin-bottom: 2ex; 4.53 -} 4.54 - 4.55 -.member_area_list .unit_head { 4.56 +.area_list .unit_head { 4.57 font-size: 120%; 4.58 font-weight: bold; 4.59 - float: left; 4.60 - clear: both; 4.61 -} 4.62 - 4.63 -.member_area_list .unit_delegatee { 4.64 - float: left; 4.65 -} 4.66 - 4.67 -.member_area_list .voting_priv_info { 4.68 - clear: left; 4.69 - margin-top: 4ex; 4.70 -} 4.71 - 4.72 -.member_area_list .more_areas { 4.73 - font-style: italic; 4.74 -} 4.75 - 4.76 -.area_list { 4.77 - margin-bottom: 4ex; 4.78 -} 4.79 - 4.80 -.member_area_list .area_list { 4.81 - margin-bottom: 0; 4.82 + margin-bottom: 1ex; 4.83 } 4.84 4.85 .area_list .area { 4.86 - line-height: 32px; 4.87 - clear: both; 4.88 -} 4.89 - 4.90 -.area_list .bargraph { 4.91 - line-height: 10px; 4.92 -} 4.93 - 4.94 -.area_list .area .delegatee { 4.95 - white-space: nowrap; 4.96 -} 4.97 - 4.98 -.area_list .area.head .phases { 4.99 - xmargin-top: 2ex; 4.100 -} 4.101 - 4.102 -.area_list .area img { 4.103 - vertical-align: middle; 4.104 -} 4.105 - 4.106 -.area_list .area .bar { 4.107 - float: left; 4.108 - padding-top: 6px 4.109 -} 4.110 - 4.111 -.area_list .area .membership, 4.112 -.area_list .area .delegatee { 4.113 - display: inline; 4.114 - width: 24px 4.115 + margin-bottom: 1ex; 4.116 } 4.117 4.118 -.area_list .area .name { 4.119 - display: inline; 4.120 - font-weight: bold; 4.121 -} 4.122 4.123 -.area_list .area .phases { 4.124 - float: right; 4.125 -} 4.126 - 4.127 -.area_list .area .phases div { 4.128 - float: left; 4.129 - width: 3em; 4.130 - text-align: right; 4.131 -} 4.132 - 4.133 -@media screen and (max-width: 480px) { 4.134 - .area_list .area .name { 4.135 - display: block; 4.136 - line-height: 100%; 4.137 - } 4.138 - 4.139 - .area_list .area .delegatee, 4.140 - .area_list .area .membership { 4.141 - float: left; 4.142 - white-space: nowrap; 4.143 - } 4.144 - .area_list .area .bar { 4.145 - display: none; 4.146 - } 4.147 -} 4.148 4.149 /************************************************************************* 4.150 * Events 4.151 @@ -1434,12 +1352,14 @@ 4.152 4.153 /* shadows */ 4.154 4.155 +.area_list .area, 4.156 .initiative_head, 4.157 .box, 4.158 div.notifications { 4.159 border-radius: 8px; 4.160 } 4.161 4.162 +.area_list .area, 4.163 .initiative_head, 4.164 .box { 4.165 border: 1px solid #aaa; 4.166 @@ -1449,12 +1369,8 @@ 4.167 padding: 1ex; 4.168 } 4.169 4.170 -.member_area_list .box { 4.171 - border: none; 4.172 - padding: 0; 4.173 -} 4.174 - 4.175 .slot_head, 4.176 +.area_list .area, 4.177 .initiative_head, 4.178 .issues .issue, 4.179 .ui_tabs_links a, 4.180 @@ -1464,7 +1380,6 @@ 4.181 .motd, 4.182 .ui_filter a.active, 4.183 .vote_info .content, 4.184 -.member_area_list, 4.185 .box, 4.186 div.notifications { 4.187 box-shadow: #777 0px 5px 5px -5px; 4.188 @@ -1473,12 +1388,6 @@ 4.189 clear: both; 4.190 } 4.191 4.192 -.member_area_list .box { 4.193 - box-shadow: none; 4.194 - -mox-box-shadow: none; 4.195 - -webkit-box-shadow: none; 4.196 -} 4.197 - 4.198 .slot_footer { 4.199 margin: 0 1%; 4.200 border-top: 1px solid #ccc;